diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7bdfcb5030..53fcae1e4e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,206 +1,108 @@ -name: Deploy +name: Deploy to Netlify on: workflow_call: inputs: environment: type: string - description: Environment to deploy to required: true - -jobs: - install-and-build: - name: NPM install and build site +jobs: + netlify-deploy: environment: ${{ inputs.environment }} runs-on: ubuntu-latest defaults: run: shell: bash working-directory: site/gatsby-site + permissions: + pull-requests: write steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - # Cache 'node_modules' and '~/.cache/Cypress' folder - - name: Cache node modules + - name: Read node modules from cache id: cache-nodemodules - uses: actions/cache@v3.0.5 + uses: actions/cache/restore@v3 env: cache-name: cache-install-folder with: - # caching node_modules path: | site/gatsby-site/node_modules - ~/.cache/Cypress key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - # Install NPM dependencies - - name: Install NPM dependencies + - name: Set up Node.js + uses: actions/setup-node@v3 + + - name: Install dependencies if: steps.cache-nodemodules.outputs.cache-hit != 'true' - uses: cypress-io/github-action@v4 - with: - working-directory: site/gatsby-site - # just perform install - runTests: false - install-command: npm ci --legacy-peer-deps + run: npm ci + + - name: Use new netlify.toml + run: | + rm -f netlify.toml + mv github-netlify.toml netlify.toml + + - name: Install Netlify CLI + run: npm install netlify-cli -g - # Build Gatbsy site - - name: Build site - run: npm run build + - name: Build using Netlify + run: netlify build --context deploy-preview --offline env: + NETLIFY_SITE_ID: ${{ vars.NETLIFY_SITE_ID }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} + CYPRESS_PROJECT_ID: ${{ vars.CYPRESS_PROJECT_ID }} CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} E2E_ADMIN_USERNAME: ${{ secrets.E2E_ADMIN_USERNAME }} E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }} ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }} - GATSBY_ALGOLIA_APP_ID: ${{ secrets.GATSBY_ALGOLIA_APP_ID }} - GATSBY_ALGOLIA_SEARCH_KEY: ${{ secrets.GATSBY_ALGOLIA_SEARCH_KEY }} - GATSBY_AVAILABLE_LANGUAGES: ${{ secrets.GATSBY_AVAILABLE_LANGUAGES }} - GATSBY_REALM_APP_ID: ${{ secrets.GATSBY_REALM_APP_ID }} + GATSBY_ALGOLIA_APP_ID: ${{ vars.GATSBY_ALGOLIA_APP_ID }} + GATSBY_ALGOLIA_SEARCH_KEY: ${{ vars.GATSBY_ALGOLIA_SEARCH_KEY }} + GATSBY_AVAILABLE_LANGUAGES: ${{ vars.GATSBY_AVAILABLE_LANGUAGES }} + GATSBY_REALM_APP_ID: ${{ vars.GATSBY_REALM_APP_ID }} GOOGLE_TRANSLATE_API_KEY: ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} MONGODB_CONNECTION_STRING: ${{ secrets.MONGODB_CONNECTION_STRING }} MONGODB_REPLICA_SET: ${{ secrets.MONGODB_REPLICA_SET }} MONGODB_TRANSLATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_TRANSLATIONS_CONNECTION_STRING }} MONGODB_MIGRATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_MIGRATIONS_CONNECTION_STRING }} GATSBY_REALM_APP_GRAPHQL_URL: ${{ secrets.GATSBY_REALM_APP_GRAPHQL_URL }} + GATSBY_PRISMIC_REPO_NAME: ${{ vars.GATSBY_PRISMIC_REPO_NAME }} + PRISMIC_ACCESS_TOKEN: ${{ secrets.PRISMIC_ACCESS_TOKEN }} + NODE_OPTIONS: --dns-result-order=ipv4first + GATSBY_ROLLBAR_TOKEN: ${{ secrets.GATSBY_ROLLBAR_TOKEN }} + SKIP_PAGE_CREATOR: ${{ vars.SKIP_PAGE_CREATOR }} + CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }} + CLOUDFLARE_R2_BUCKET_NAME: ${{ secrets.CLOUDFLARE_R2_BUCKET_NAME }} + GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL: ${{ vars.GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL }} + CLOUDFLARE_R2_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + CLOUDFLARE_R2_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + REALM_GRAPHQL_API_KEY: ${{ secrets.REALM_GRAPHQL_API_KEY }} - # Extract commit hash to use as a cache key - - name: Extract commit hash - shell: bash - run: echo "##[set-output name=commit;]$(echo ${GITHUB_SHA})" - id: extract_commit_hash - - # Cache 'public' folder - - name: Cache public folder - uses: actions/cache@v3.0.5 - env: - cache-name: cache-public - with: - path: | - site/gatsby-site/public - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ steps.extract_commit_hash.outputs.commit }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - test: - name: Run Cypress tests - environment: ${{ inputs.environment }} - runs-on: ubuntu-latest - needs: install-and-build - defaults: - run: - shell: bash + - name: Upload to netlify + id: deploy-netlify working-directory: site/gatsby-site - strategy: - # when one test fails, DO NOT cancel the other - # containers, because this will kill Cypress processes - # leaving the Dashboard hanging ... - # https://github.com/cypress-io/github-action/issues/48 - fail-fast: false - matrix: - # run 10 copies of the current job in parallel - containers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - # stop the job if it runs over 20 minutes - # to prevent a hanging process from using all your CI minutes - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v2 - - # Cache node_modules folder - - name: Cache node modules - id: cache-nodemodules-2 - uses: actions/cache@v3.0.5 - env: - cache-name: cache-install-folder - with: - # caching node_modules - path: | - site/gatsby-site/node_modules - ~/.cache/Cypress - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - # Install NPM dependencies - - name: Install NPM dependencies - if: steps.cache-nodemodules-2.outputs.cache-hit != 'true' - uses: cypress-io/github-action@v4 - with: - working-directory: site/gatsby-site - # just perform install - runTests: false - install-command: npm ci --legacy-peer-deps - - # Extract commit hash to use as a cache key - - name: Extract commit hash - shell: bash - run: echo "##[set-output name=commit;]$(echo ${GITHUB_SHA})" - id: extract_commit_hash - - # Cache 'public' folder - - name: Cache public folder - uses: actions/cache@v3.0.5 + run: | + set -e + OUTPUT=$(bash -c "netlify deploy --json --alias=pr-${{ github.event.pull_request.number }}" | tr '\n' ' ') + set +e + NETLIFY_OUTPUT=$(echo "$OUTPUT") + echo "deploy_log=$NETLIFY_OUTPUT" >> $GITHUB_OUTPUT env: - cache-name: cache-public - with: - path: | - site/gatsby-site/public - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ steps.extract_commit_hash.outputs.commit }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ vars.NETLIFY_SITE_ID }} - # Extract branch name - - name: Extract branch name - shell: bash - run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" - id: extract_branch - - # Run all Cypress tests - - name: Cypress run - uses: cypress-io/github-action@v4 + - name: Comment on PR + uses: actions/github-script@v5 with: - working-directory: site/gatsby-site - # we have already installed all dependencies above - install: false - config-file: cypress.config.js - record: true - parallel: true - group: "Cypress e2e tests" - tag: ${{ steps.extract_branch.outputs.branch }} - start: npm run serve - wait-on: http://localhost:8000/ - # wait for 10 minutes for the server to respond - wait-on-timeout: 600 - env: - # Recommended: pass the GitHub token lets this action correctly - # determine the unique run id necessary to re-run the checks - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - E2E_ADMIN_USERNAME: ${{ secrets.E2E_ADMIN_USERNAME }} - E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }} - ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }} - GATSBY_ALGOLIA_APP_ID: ${{ secrets.GATSBY_ALGOLIA_APP_ID }} - GATSBY_ALGOLIA_SEARCH_KEY: ${{ secrets.GATSBY_ALGOLIA_SEARCH_KEY }} - GATSBY_AVAILABLE_LANGUAGES: ${{ secrets.GATSBY_AVAILABLE_LANGUAGES }} - GATSBY_REALM_APP_ID: ${{ secrets.GATSBY_REALM_APP_ID }} - GOOGLE_TRANSLATE_API_KEY: ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} - MONGODB_CONNECTION_STRING: ${{ secrets.MONGODB_CONNECTION_STRING }} - MONGODB_REPLICA_SET: ${{ secrets.MONGODB_REPLICA_SET }} - MONGODB_TRANSLATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_TRANSLATIONS_CONNECTION_STRING }} - MONGODB_MIGRATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_MIGRATIONS_CONNECTION_STRING }} - GATSBY_REALM_APP_GRAPHQL_URL: ${{ secrets.GATSBY_REALM_APP_GRAPHQL_URL }} - # Since this is triggered on a pull request, we set the commit message to the pull request title - COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} - + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const deployOutput = `${{ steps.deploy-netlify.outputs.deploy_log }}`; + const deployData = JSON.parse(deployOutput); + const comment = `🚀 Deployed to Netlify!\n\n✅ Build Log: \n${deployData.logs}\n\n🔗 Preview URL: ${deployData.deploy_url}`; + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000000..5820fdcf5d --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,52 @@ +name: Deploy Preview Branch + +on: + pull_request_target: + branches: + - staging + types: [opened, synchronize, reopened] +jobs: + permissions-check: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@v2 + with: + require: write + username: ${{ github.triggering_actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.triggering_actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + echo "Job originally triggered by ${{ github.actor }}" + exit 1 + + call-test-build: + if: ${{ !failure() }} + uses: ./.github/workflows/test-build.yml + needs: permissions-check + secrets: inherit + with: + environment: staging + + call-test: + if: ${{ !failure() }} + uses: ./.github/workflows/test.yml + needs: call-test-build + secrets: inherit + with: + environment: staging + + call-deploy: + if: ${{ !failure() }} + uses: ./.github/workflows/deploy.yml + needs: permissions-check + secrets: inherit + permissions: + pull-requests: write + with: + environment: staging diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml new file mode 100644 index 0000000000..ce4c83a620 --- /dev/null +++ b/.github/workflows/test-build.yml @@ -0,0 +1,91 @@ +name: Build test site +on: + workflow_call: + inputs: + environment: + type: string + required: true + +jobs: + test: + name: Build site for testing + environment: ${{ inputs.environment }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Read node modules from cache + id: cache-nodemodules + uses: actions/cache/restore@v3 + env: + cache-name: cache-install-folder + with: + path: | + site/gatsby-site/node_modules + ~/.cache/Cypress + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + + - name: Install NPM dependencies + if: steps.cache-nodemodules.outputs.cache-hit != 'true' + uses: cypress-io/github-action@v6 + with: + working-directory: site/gatsby-site + runTests: false + install-command: npm ci + + - name: Use tests specific netlify.toml + run: | + rm -f netlify.toml + mv tests-netlify.toml netlify.toml + working-directory: site/gatsby-site + + - name: Install Netlify CLI + run: npm install netlify-cli -g + + - name: Build using Netlify + run: netlify build --context deploy-preview --offline + working-directory: site/gatsby-site + env: + INSTRUMENT: true + NETLIFY_SITE_ID: ${{ vars.NETLIFY_SITE_ID }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CYPRESS_PROJECT_ID: ${{ vars.CYPRESS_PROJECT_ID }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + E2E_ADMIN_USERNAME: ${{ secrets.E2E_ADMIN_USERNAME }} + E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }} + ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }} + GATSBY_ALGOLIA_APP_ID: ${{ vars.GATSBY_ALGOLIA_APP_ID }} + GATSBY_ALGOLIA_SEARCH_KEY: ${{ vars.GATSBY_ALGOLIA_SEARCH_KEY }} + GATSBY_AVAILABLE_LANGUAGES: ${{ vars.GATSBY_AVAILABLE_LANGUAGES }} + GATSBY_REALM_APP_ID: ${{ vars.GATSBY_REALM_APP_ID }} + GOOGLE_TRANSLATE_API_KEY: ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} + MONGODB_CONNECTION_STRING: ${{ secrets.MONGODB_CONNECTION_STRING }} + MONGODB_REPLICA_SET: ${{ secrets.MONGODB_REPLICA_SET }} + MONGODB_TRANSLATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_TRANSLATIONS_CONNECTION_STRING }} + MONGODB_MIGRATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_MIGRATIONS_CONNECTION_STRING }} + GATSBY_REALM_APP_GRAPHQL_URL: ${{ secrets.GATSBY_REALM_APP_GRAPHQL_URL }} + GATSBY_PRISMIC_REPO_NAME: ${{ vars.GATSBY_PRISMIC_REPO_NAME }} + PRISMIC_ACCESS_TOKEN: ${{ secrets.PRISMIC_ACCESS_TOKEN }} + NODE_OPTIONS: --dns-result-order=ipv4first + GATSBY_ROLLBAR_TOKEN: ${{ secrets.GATSBY_ROLLBAR_TOKEN }} + SKIP_PAGE_CREATOR: ${{ vars.SKIP_PAGE_CREATOR }} + CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }} + CLOUDFLARE_R2_BUCKET_NAME: ${{ secrets.CLOUDFLARE_R2_BUCKET_NAME }} + GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL: ${{ vars.GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL }} + CLOUDFLARE_R2_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + CLOUDFLARE_R2_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + REALM_GRAPHQL_API_KEY: ${{ secrets.REALM_GRAPHQL_API_KEY }} + + - name: Cache build + uses: actions/cache/save@v3 + env: + cache-name: cache-build-folder + with: + path: | + site/gatsby-site/public + site/gatsby-site/.cache/functions + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index daedff44dc..2010da2399 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,19 +1,116 @@ -name: Deploy Staging - +name: Run tests on: - push: - branches: - - feature-github-tests + workflow_call: + inputs: + environment: + type: string + required: true jobs: - # call-realm: - # uses: ./.github/workflows/realm.yml - # secrets: inherit - # with: - # environment: staging - call-deploy: - uses: ./.github/workflows/deploy.yml - secrets: inherit - with: - environment: staging - # needs: call-realm + test: + name: Run Cypress tests + environment: ${{ inputs.environment }} + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: site/gatsby-site + strategy: + # when one test fails, DO NOT cancel the other + # containers, because this will kill Cypress processes + # leaving the Dashboard hanging ... + # https://github.com/cypress-io/github-action/issues/48 + fail-fast: false + matrix: + # run 4 copies of the current job in parallel + containers: [1, 2, 3, 4] + # stop the job if it runs over 20 minutes + # to prevent a hanging process from using all your CI minutes + timeout-minutes: 40 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Read node modules from cache + id: cache-nodemodules + uses: actions/cache/restore@v3 + env: + cache-name: cache-install-folder + with: + path: | + site/gatsby-site/node_modules + ~/.cache/Cypress + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + + - name: Install NPM dependencies + if: steps.cache-nodemodules.outputs.cache-hit != 'true' + uses: cypress-io/github-action@v6 + with: + working-directory: site/gatsby-site + runTests: false + install-command: npm ci + + - name: Restore build cache + uses: actions/cache/restore@v3 + env: + cache-name: cache-build-folder + with: + path: | + site/gatsby-site/public + site/gatsby-site/.cache/functions + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ github.event.pull_request.head.sha }} + + - name: Extract branch name + shell: bash + run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV + id: extract_branch + + - name: Cypress run + uses: cypress-io/github-action@v6 + with: + working-directory: site/gatsby-site + install: false + config-file: cypress.config.js + record: true + parallel: true + group: "Cypress e2e tests" + tag: ${{ steps.extract_branch.outputs.branch }} + start: node node_modules/.bin/gatsby serve -p 8000 -H 127.0.0.1 + wait-on: http://127.0.0.1:8000 + wait-on-timeout: 60 + env: + # Recommended: pass the GitHub token lets this action correctly + # determine the unique run id necessary to re-run the checks + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CYPRESS_PROJECT_ID: ${{ vars.CYPRESS_PROJECT_ID }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + E2E_ADMIN_USERNAME: ${{ secrets.E2E_ADMIN_USERNAME }} + E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }} + ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }} + GATSBY_ALGOLIA_APP_ID: ${{ vars.GATSBY_ALGOLIA_APP_ID }} + GATSBY_ALGOLIA_SEARCH_KEY: ${{ vars.GATSBY_ALGOLIA_SEARCH_KEY }} + GATSBY_AVAILABLE_LANGUAGES: ${{ vars.GATSBY_AVAILABLE_LANGUAGES }} + GATSBY_REALM_APP_ID: ${{ vars.GATSBY_REALM_APP_ID }} + GOOGLE_TRANSLATE_API_KEY: ${{ secrets.GOOGLE_TRANSLATE_API_KEY }} + MONGODB_CONNECTION_STRING: ${{ secrets.MONGODB_CONNECTION_STRING }} + MONGODB_REPLICA_SET: ${{ secrets.MONGODB_REPLICA_SET }} + MONGODB_TRANSLATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_TRANSLATIONS_CONNECTION_STRING }} + MONGODB_MIGRATIONS_CONNECTION_STRING: ${{ secrets.MONGODB_MIGRATIONS_CONNECTION_STRING }} + GATSBY_REALM_APP_GRAPHQL_URL: ${{ secrets.GATSBY_REALM_APP_GRAPHQL_URL }} + GATSBY_ROLLBAR_TOKEN: ${{ secrets.GATSBY_ROLLBAR_TOKEN }} + INSTRUMENT: true + # Since this is triggered on a pull request, we set the commit message to the pull request title + COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} + CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }} + CLOUDFLARE_R2_BUCKET_NAME: ${{ secrets.CLOUDFLARE_R2_BUCKET_NAME }} + GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL: ${{ vars.GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL }} + CLOUDFLARE_R2_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + CLOUDFLARE_R2_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + REALM_GRAPHQL_API_KEY: ${{ secrets.REALM_GRAPHQL_API_KEY }} + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4433d887c5..a0f951d474 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,5 @@ package-lock.json !themes/gatsby-starter-notes-theme/package-lock.json !themes/gatsby-starter-theme/package-lock.json !themes/gatsby-starter-theme-workspace/package-lock.json + +.nyc_output \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index 4321e60132..64a50330d1 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ AI Incident Database: A program for the archiving of artificial intelligence incidents. -Copyright (C) 2022 Responsible AI Collaborative, Inc. +Copyright (C) 2023 Responsible AI Collaborative, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 40d13219c0..8de84e38e8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,17 @@ -# Artificial Intelligence Incident Database (AIID) +

+ + + + + + +

+

+ Artificial Intelligence Incident Database +

[![Netlify Status](https://api.netlify.com/api/v1/badges/9eb0dda2-916c-46f9-a0bd-9ddab3879c6e/deploy-status)](https://app.netlify.com/sites/aiid/deploys) +[![Slack Link](https://img.shields.io/badge/Join%20the%20RAIC%20Slack!-purple?logo=slack)](https://forms.gle/v7UHJvEkYSJQ7jHj7) Information about the goals and organization of the AI Incident Database can be found on the [production website](https://incidentdatabase.ai/). This page concentrates on onboarding for the following types of contributions to the database, @@ -448,6 +459,12 @@ GATSBY_EXCLUDE_DATASTORE_FROM_BUNDLE=1 # specific to Netlify, for large sites GATSBY_CPU_COUNT=2 # limits the number of Gatsby threads, helping with deployment stability NODE_VERSION=18 # this is required by Gatsby v5 NODE_OPTIONS=--max-old-space-size=4096 # increase default heap size to prevent crashes during build +# The following "CLOUDFLARE_R2" variables are required to create the /research/snapshots/ page +CLOUDFLARE_R2_ACCOUNT_ID=[The Cloudflare R2 account ID (e.g.: 8f4144a9d995a9921d0200db59f6a00e)] +CLOUDFLARE_R2_ACCESS_KEY_ID=[The Cloudflare R2 access key ID (e.g.: 7aa73208bc89cee3195879e578b291ee)] +CLOUDFLARE_R2_SECRET_ACCESS_KEY=[The Cloudflare R2 secret access key] +CLOUDFLARE_R2_BUCKET_NAME=[The Cloudflare R2 bucket name (e.g.: 'aiid-public')] +GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL=[The Cloudflare R2 public bucket URL (e.g.: https://pub-daddb16dc28841779b83690f75eb5c58.r2.dev)] ``` ### Github Actions Two workflows take care of deploying the Realm app to both `production` and `staging` environments, defined in `realm-production.yml` and `realm-staging.yml`. Each workflow looks for environment variables defined in a GitHub Environment named `production` and `staging`. diff --git a/mongo.md b/mongo.md index f49823cd48..82d05541fa 100644 --- a/mongo.md +++ b/mongo.md @@ -54,10 +54,10 @@ Administering data requires administrative access to the database. This access i ### Dates -* `date_downloaded`:`2019-07-25` # (String) Date the report was downloaded. -* `date_submitted`:`2019-07-25` # (String) Date the report was submitted to the AIID. This determines citation order. -* `date_modified`: `2019-07-25` # (String) Date the report was edited. -* `date_published`: `2019-07-25` # (String) The publication date of the report. +* `date_downloaded`:`2019-07-25` # (Date) Date the report was downloaded. +* `date_submitted`:`2019-07-25` # (Date) Date the report was submitted to the AIID. This determines citation order. +* `date_modified`: `2019-07-25` # (Date) Date the report was edited. +* `date_published`: `2019-07-25` # (Date) The publication date of the report. * `epoch_incident_date`: `1564016400` # (Int) Date the incident occurred in the Unix Epoch. * `epoch_date_downloaded`:`1564016400` # (Int) Date the report was downloaded in the Unix Epoch. * `epoch_date_submitted`:`1564016400` # (Int) Date the report was submitted to the AIID in the Unix Epoch. diff --git a/site/gatsby-site/blog/deepfakes-child-safety/images/image_doj.jpg b/site/gatsby-site/blog/deepfakes-child-safety/images/image_doj.jpg new file mode 100644 index 0000000000..9cc6599e11 Binary files /dev/null and b/site/gatsby-site/blog/deepfakes-child-safety/images/image_doj.jpg differ diff --git a/site/gatsby-site/blog/deepfakes-child-safety/index.es.mdx b/site/gatsby-site/blog/deepfakes-child-safety/index.es.mdx new file mode 100644 index 0000000000..9ed57828b0 --- /dev/null +++ b/site/gatsby-site/blog/deepfakes-child-safety/index.es.mdx @@ -0,0 +1,54 @@ +--- +title: 'Deepfakes y seguridad infantil: encuesta y análisis de incidentes y respuestas de 2023' +metaTitle: 'Deepfakes y seguridad infantil: encuesta y análisis de incidentes y respuestas de 2023' +metaDescription: "" +date: '2024-01-09' +image: './images/image_doj.jpg' +author: 'Daniel Atherton' +slug: '/blog/deepfakes-and-child-safety' +aiTranslated: true +--- + +**AVISO LEGAL:** Esta publicación no es un consejo o comentario legal y no debe interpretarse como tal. + +En 2023 se produjo un aumento de los materiales de abuso sexual infantil (CSAM) generados por IA, junto con procesamientos de los infractores, una variedad de intentos legislativos para combatir los deepfakes de IA dirigidos a menores y la [orden ejecutiva sobre inteligencia artificial de la administración Biden](https://www.whitehouse.gov/briefing-room/presidential-actions/2023/10/30/executive-order-on-the-safe-secure-and-trustworthy-development-and-use-of-artificial-intelligence/). + +Los deepfakes se pueden clasificar en términos generales en dos categorías principales, cada una con su propio subconjunto relacionado con CSAM. La primera categoría incluye deepfakes de individuos reales, donde el daño predominante asociado con CSAM surge de la generación de pornografía deepfake con niños reales. La segunda categoría abarca los deepfakes en los que los sujetos son completamente virtuales pero convincentemente realistas. En esta categoría, las preocupaciones sobre CSAM están relacionadas principalmente con la creación de medios audiovisuales sintéticos inapropiados que representan niños virtuales. En conjunto, estas dos categorías taxonómicas demuestran las diversas formas preocupantes en que se puede emplear la tecnología deepfake, especialmente en la generación y proliferación de CSAM. + +Este artículo proporciona una instantánea de parte del trabajo de la base de datos de incidentes de IA en el seguimiento de estos incidentes emergentes, junto con un estudio de algunas de las respuestas legislativas incipientes. + +Tres casos judiciales específicos (uno de Corea del Sur, uno de Quebec y uno de Carolina del Norte) se centran en hombres que utilizaron inteligencia artificial para generar pornografía infantil ultrafalsa. + +En [Corea del Sur](https://incidentdatabase.ai/cite/600), el acusado anónimo generó 360 imágenes de niños en situaciones sexuales. Durante el juicio, los fiscales enfatizaron que la definición legal de material de explotación sexual debería extenderse a representaciones que involucren a “humanos virtuales” y no limitarse a imágenes de niños reales. Argumentaron que con la llegada de la tecnología de “alto nivel”, como la inteligencia artificial, las imágenes lo suficientemente realistas como para parecerse a menores reales pueden constituir contenido sexualmente abusivo. Esta postura fue respaldada por el fallo del caso, alineándose con el punto de vista del fiscal sobre las capacidades tecnológicas modernas y sus implicaciones en la ley. + +En el [caso Quebec](https://incidentdatabase.ai/cite/604), Steven Larouche, de Sherbrooke, fue condenado a más de tres años de prisión por producir pornografía infantil sintética utilizando tecnología deepfake. Admitió haber creado al menos siete vídeos superponiendo caras a otros cuerpos. Además, Larouche fue declarado culpable de posesión de una vasta colección de pornografía infantil, lo que le llevó a cuatro años y medio adicionales de prisión. El juez del tribunal provincial Benoit Gagnon señaló que este es el primer caso de explotación infantil relacionado con deepfake del país y expresó su preocupación por el posible uso indebido de esta tecnología para manipular imágenes de niños en las redes sociales. + +El [caso de Carolina del Norte](https://incidentdatabase.ai/cite/605) involucró al psiquiatra infantil David Tatum. Entre sus otros cargos, la evidencia presentada en su juicio reveló que Tatum empleó IA para transformar imágenes vestidas de menores en material explícito, incluida la alteración de fotografías de un baile escolar y una celebración del primer día de clases utilizando una aplicación de IA basada en la web. + +El análisis del discurso de los medios arroja una divergencia en los tipos de incidentes que surgen como resultado de esta tecnología. Los casos anteriores se alinean con informes de larga data sobre depredadores infantiles y sus sentencias, aunque con la presencia emergente de IA como parte del perfil criminal general. A continuación, dos casos importantes de diferente tipo demuestran la proliferación en las comunidades locales (ya sean ciudades o escuelas) del uso de imágenes de niñas en pornografía deepfake. + +En [Almendralejo, España](https://incidentdatabase.ai/cite/610), se utilizó IA para crear y distribuir imágenes de niñas en las que aparecían desnudas. Según los informes, las imágenes, en las que aparecen varias chicas locales, se difundieron ampliamente, lo que dio lugar a una investigación policial. Las madres de las niñas concienciaron sobre el tema, preocupadas por la posibilidad de subir estas imágenes a sitios pornográficos. Los funcionarios regionales confirmaron que hay una investigación en curso y que se han identificado algunos sospechosos. Este incidente, descrito como violencia digital de género, generó una condena generalizada. + +De manera similar, en [Westfield High School en Nueva Jersey](https://incidentdatabase.ai/cite/597), las imágenes pornográficas de estudiantes generadas por IA causaron una angustia significativa y provocaron una investigación. Dorota Mani, cuya hija Francesca fue atacada, expresó su preocupación por el impacto de la IA en los niños y presentó un informe policial, y los dos [aparecieron en CNN](https://www.cnn.com/2023/11/ 04/us/new-jersey-high-school-deepfake-porn/index.html) para discutir el incidente. La respuesta de la escuela puede ser vista con el tiempo como uno de los primeros estudios de caso importantes para responder a estos incidentes. Casi al mismo tiempo que Westfield, la escuela secundaria Issaquah en el estado de Washington estaba experimentando [su propio problema similar](https://incidentdatabase.ai/cite/617) con deepfakes. + +La mayoría de los ejemplos analizados anteriormente parecen implicar principalmente la apropiación indebida de retratos de niños reales. Por el contrario, el caso de Corea del Sur es un ejemplo de tecnología utilizada para generar medios audiovisuales sintéticos que representan a niños que no existen en la vida real, lo que también presenta problemas importantes. + +Recientemente, [un incidente](https://incidentdatabase.ai/cite/576/) salió a la luz cuando un usuario de LinkedIn informó que una aplicación de inteligencia artificial conocida como PicSo_ai estaba generando contenido inapropiado, de manera alarmante, enfocado en "niñas". Este no fue un caso aislado. Una búsqueda en Instagram recomendó autosugerencias preocupantes relacionadas con imágenes de niñas menores de edad generadas por IA, marcadas como “populares”. Este descubrimiento apunta hacia un patrón preocupante en el que la IA se está explotando en una zona gris y donde, según [una investigación reciente del *Wall Street Journal*](https://www.wsj.com/tech/meta-instagram-video-algorithm-children-adult-sexual-content-72874155?mod=hp_lead_pos7), el algoritmo Reels de Instagram recomendaba contenido sexualmente sugerente y explícito relacionado con niños y adultos a los usuarios que seguían a jóvenes influencers. Esto generó importantes preocupaciones sobre la moderación y la seguridad del contenido de la plataforma, y los anuncios de las principales marcas aparecen junto a dicho contenido. A pesar de los esfuerzos de Meta por implementar herramientas de seguridad, los desafíos en la curación algorítmica de contenido y la seguridad digital en las redes sociales son persistentes. + +*The Wall Street Journal* también [informado recientemente](https://www.wsj.com/tech/facebook-and-instagram-steer-predators-to-children-new-mexico-attorney-general-alleges-in-demand-b76a5b04?mod=Searchresults_pos1&page=1) sobre el Fiscal General de Nuevo México que presentó una demanda contra Meta, alegando que los algoritmos de Facebook e Instagram dirigieron a los depredadores y al contenido pornográfico a cuentas de prueba de temas menores. La investigación involucró imágenes de niños ficticios generadas por IA, lo que resultó en que las cuentas recibieran mensajes explícitos y proposiciones sexuales. La demanda afirma que las plataformas de Meta se han convertido en un mercado para depredadores y critica su falta de protección a los usuarios menores de edad, citando varios casos penales de explotación a través de estas plataformas. En este caso, fueron los investigadores quienes generaron las imágenes de los menores ficticios, adaptando las nuevas tecnologías a antiguas técnicas en este ámbito específico de la aplicación de la ley. + +Un [estudio reciente](https://stacks.stanford.edu/file/druid:kh752sm9123/ml_training_data_csam_report-2023-12-21.pdf) realizado por David Thiel del Stanford Internet Observatory detalla la presencia de CSAM en los datos de entrenamiento de Modelos generativos de aprendizaje automático, centrándose en el conjunto de datos LAION-5B utilizado para modelos como Difusión estable. A través de varios métodos, incluidos los clasificadores PhotoDNA y ML, Thiel identificó numerosos casos nuevos y conocidos de CSAM en el conjunto de datos. Los hallazgos son oportunos ya que muestran la necesidad de prácticas de capacitación de modelos y curación de datos más rigurosas para evitar la perpetuación de contenido dañino, en línea con las preocupaciones planteadas por los incidentes en las plataformas de redes sociales y enfatizando la importancia de un mayor desarrollo responsable de la IA en este sentido. frente. + +Actualmente, los esfuerzos legales para abordar los deepfakes de CSAM han sido reactivos y poco sistemáticos. Sin embargo, la orden ejecutiva del presidente Biden sobre la IA tiene como objetivo establecer normas estrictas para prevenir el uso indebido de la IA, centrándose en la seguridad nacional y la seguridad individual, lo que implica autenticar el contenido digital y etiquetar los medios sintéticos, especialmente para proteger a los niños de los daños provocados por la IA. Los desarrolladores deben compartir los resultados de las pruebas de seguridad de la IA antes de su uso público, centrándose en problemas como la creación de CSAM. La orden dirige el desarrollo de estándares para la autenticación de contenido y la detección de IA, y aborda el CSAM generado por IA y las imágenes sexualizadas no consensuales. Antes de la orden ejecutiva, [U.S. Los fiscales generales instaron al Congreso](https://www.scag.gov/media/pvehppkm/54-state-ags-urge-study-of-ai-and-harmful-impacts-on-children.pdf) a investigar el papel de la IA. en la explotación infantil, enfatizando la necesidad de una legislación integral sobre privacidad de datos. + +Hasta el momento, [no existe ningún proyecto de ley federal general](https://www.nbcnews.com/news/us-news/little-recourse-teens-girls-victimized-ai-deepfake-nudes-rcna126399), pero se han realizado esfuerzos. (por ejemplo, [Ley de Responsabilidad H.R.3230 DEEP FAKES](https://www.congress.gov/bill/116th-congress/house-bill/3230)). A continuación se muestran cuatro ejemplos de legislación a nivel estatal: + +* [California, AB 602, Sección 1708.86](https://pluralpolicy.com/app/legislative-tracking/bill/details/state-ca-20192020-ab602/202869): Modifica la ley existente no solo para permitir acciones contra aquellos que crean y comparten material sexualmente explícito sin consentimiento, pero también enfatiza específicamente acciones legales contra personas que distribuyen dicho material que ellos no crearon, siempre que sean conscientes de que la persona representada no dio su consentimiento para su creación. Esta faceta del proyecto de ley es importante ya que extiende la responsabilidad a quienes propagan contenido explícito no consensuado, independientemente de su papel en su creación original. + +* [Florida, CS/CS/SB 1798](https://www.flsenate.gov/Session/Bill/2022/1798/?Tab=BillText): Aborda los delitos sexuales que involucran imágenes alteradas, específicamente deepfakes. Penaliza la promoción de representaciones sexuales alteradas sin consentimiento, definiéndolas como delitos graves de tercer grado. El proyecto de ley amplía la definición de “pornografía infantil” para incluir imágenes modificadas digitalmente que representan a menores en conducta sexual. También aumenta las penas por ciberacoso sexual y contacto sexual con animales, incluida la posesión de muñecas sexuales con apariencia de niños. Se hacen exenciones para ciertas entidades, como los medios de comunicación y las fuerzas del orden, bajo condiciones específicas. El proyecto de ley enfatiza la protección de los menores y abordar las tendencias emergentes de explotación sexual digital. + +* [Illinois, Proyecto de Ley 2123](https://legiscan.com/IL/bill/HB2123/2023): Redefine “imagen sexual” en la Ley de Remedios Civiles para la Difusión No Consensual de Imágenes Sexuales Privadas para incluir imágenes que muestren desnudez o conducta, ya sea real o alterada digitalmente. Permite a las personas que aparecen en estas imágenes emprender acciones legales contra quienes comparten o amenazan con compartir estas imágenes, especialmente cuando no hay consentimiento. La enmienda especifica que revelar la alteración digital de una imagen no es una defensa. También elimina ciertas responsabilidades de los servicios informáticos interactivos y aclara que agregar un mensaje político a tales imágenes no las convierte en un asunto de preocupación pública. Además, los tribunales pueden dictar órdenes para detener la difusión de estas imágenes. + +* [Virginia, Código 18.2-386.2](https://law.lis.virginia.gov/vacode/title18.2/chapter8/section18.2-386.2/): convierte en un delito menor de Clase 1 compartir o vender desnudos maliciosamente o imágenes sexualmente explícitas de alguien sin su consentimiento, especialmente si se hacen para acosar, coaccionar o intimidar. Esto incluye imágenes que han sido alteradas digitalmente para representar a una persona. Los proveedores de servicios de Internet no se hacen responsables del contenido compartido por otros. Se pueden emprender acciones legales donde se produjo el hecho ilícito o donde se manipuló la imagen. También pueden aplicarse otros cargos legales. + +En la base de datos de incidentes de IA (AIID), hemos estado catalogando e investigando con preocupación estos incidentes recientes de CSAM. Como toda tecnología, la IA generativa plantea riesgos y oportunidades, y los riesgos para los niños en este caso son graves. Si desea unirse a nosotros en nuestra misión de documentar incidentes de IA con el objetivo de aprender de los errores del pasado para mitigar riesgos futuros, puede conectarse con nosotros a través de nuestra página de [contacto](https://incidentdatabase.ai/contact/) . Agradecemos los envíos que informen sobre todos y cada uno de los incidentes de IA utilizando nuestra página [envío](https://incidentdatabase.ai/apps/submit/); sin embargo, tenga en cuenta que, si bien rastreamos y analizamos activamente las tendencias e incidentes de CSAM, el AIID no es el destino directo para informar sobre CSAM real. Dirija la denuncia de CSAM al Departamento de Justicia [Sección de Obscenidad y Explotación Infantil](https://www.justice.gov/criminal/criminal-ceos/report-violations). \ No newline at end of file diff --git a/site/gatsby-site/blog/deepfakes-child-safety/index.fr.mdx b/site/gatsby-site/blog/deepfakes-child-safety/index.fr.mdx new file mode 100644 index 0000000000..702ee8317f --- /dev/null +++ b/site/gatsby-site/blog/deepfakes-child-safety/index.fr.mdx @@ -0,0 +1,54 @@ +--- +title: 'Deepfakes et sécurité des enfants : une enquête et une analyse des incidents et des réponses de 2023' +metaTitle: 'Deepfakes et sécurité des enfants : une enquête et une analyse des incidents et des réponses de 2023' +metaDescription: "" +date: '2024-01-09' +image: './images/image_doj.jpg' +author: 'Daniel Atherton' +slug: '/blog/deepfakes-and-child-safety' +aiTranslated: true +--- + +**AVERTISSEMENT :** Cet article ne constitue pas un avis ou un commentaire juridique et ne doit pas être interprété comme tel. + +L'année 2023 a été marquée par une augmentation du nombre de matériels pédophiles générés par l'IA, ainsi que par des poursuites contre les contrevenants, diverses tentatives législatives pour lutter contre les deepfakes de l'IA ciblant les mineurs et le [décret exécutif sur l'intelligence artificielle de l'administration Biden](https://www.whitehouse.gov/briefing-room/presidential-actions/2023/10/30/executive-order-on-the-safe-secure-and-trustworthy-development-and-use-of-artificial-intelligence/). + +Les deepfakes peuvent être globalement classés en deux catégories principales, chacune avec son propre sous-ensemble lié au CSAM. La première catégorie comprend les deepfakes d’individus réels, où le préjudice prédominant associé au CSAM provient de la génération de deepfake pornographie mettant en scène de vrais enfants. La deuxième catégorie englobe les deepfakes dont les sujets sont entièrement virtuels mais d’un réalisme convaincant. Dans cette catégorie, les préoccupations liées au CSAM sont principalement liées à la création de supports audiovisuels synthétiques inappropriés représentant des enfants virtuels. Collectivement, ces deux catégories taxonomiques démontrent les différentes manières troublantes dont la technologie deepfake peut être utilisée, en particulier dans la génération et la prolifération de CSAM. + +Cet article fournit un aperçu de certains des travaux de la base de données d'incidents d'IA pour suivre ces incidents émergents, ainsi qu'une enquête sur certaines des réponses législatives naissantes. + +Trois affaires judiciaires spécifiques – une en Corée du Sud, une au Québec et une en Caroline du Nord – se concentrent sur des hommes qui ont utilisé l’IA pour générer de la fausse pornographie d’enfants. + +En [Corée du Sud](https://incidentdatabase.ai/cite/600), l'accusé anonyme a généré 360 images d'enfants dans des situations sexuelles. Au cours du procès, les procureurs ont souligné que la définition légale du matériel d’exploitation sexuelle devrait s’étendre aux représentations impliquant des « humains virtuels » et ne pas se limiter aux images d’enfants réels. Ils ont fait valoir qu’avec l’avènement de technologies « de haut niveau », telles que l’IA, des images suffisamment réalistes pour ressembler à de véritables mineurs peuvent constituer un contenu sexuellement abusif. Cette position a été soutenue par la décision rendue dans cette affaire, qui s’aligne sur le point de vue du procureur sur les capacités technologiques modernes et leurs implications juridiques. + +Dans l'[Affaire du Québec](https://incidentdatabase.ai/cite/604), Steven Larouche de Sherbrooke a été condamné à plus de trois ans de prison pour avoir produit de la pornographie juvénile synthétique à l'aide de la technologie deepfake. Il a admis avoir créé au moins sept vidéos en superposant des visages sur d'autres corps. De plus, Larouche a été reconnu coupable de possession d'une vaste collection de pornographie juvénile, ce qui lui a valu quatre ans et demi de prison supplémentaires. Le juge de la Cour provinciale Benoit Gagnon a souligné qu'il s'agissait du premier cas d'exploitation d'enfants lié au deepfake au pays, exprimant ses inquiétudes quant à l'utilisation abusive potentielle de cette technologie pour manipuler des images d'enfants sur les réseaux sociaux. + +L'[affaire de Caroline du Nord](https://incidentdatabase.ai/cite/605) impliquait le pédopsychiatre David Tatum. Parmi ses autres accusations, les preuves présentées lors de son procès ont révélé que Tatum a utilisé l'IA pour transformer des images habillées de mineurs en matériel explicite, notamment en modifiant des photos d'un bal à l'école et d'une célébration du premier jour d'école à l'aide d'une application d'IA basée sur le Web. + +L’analyse du discours médiatique révèle une divergence dans les types d’incidents résultant de cette technologie. Les cas ci-dessus correspondent à des reportages de longue date sur les prédateurs d’enfants et leurs condamnations, bien qu’avec la présence émergente de l’IA dans le profil global de la criminalité. Ci-dessous, deux cas majeurs d’une nature différente démontrent la prolifération dans les communautés locales (qu’il s’agisse de villes ou d’écoles) de portraits de filles utilisés dans la pornographie deepfake. + +À [Almendralejo, Espagne](https://incidentdatabase.ai/cite/610), l'IA a été utilisée pour créer et diffuser des images de jeunes filles dans lesquelles elles devaient apparaître nues. Les images, impliquant plusieurs filles du quartier, auraient été largement partagées, ce qui aurait donné lieu à une enquête policière. Les mères des filles ont sensibilisé à ce problème, s'inquiétant du potentiel de téléchargement de ces images sur des sites pornographiques. Les responsables régionaux ont confirmé qu'une enquête était en cours et que certains suspects ont été identifiés. Cet incident, décrit comme une violence numérique basée sur le genre, a suscité une large condamnation. + +De même, au [Westfield High School du New Jersey](https://incidentdatabase.ai/cite/597), des images pornographiques d'élèves générées par l'IA ont provoqué une détresse considérable et ont déclenché une enquête. Dorota Mani, dont la fille Francesca a été ciblée, a exprimé ses inquiétudes quant à l'impact de l'IA sur les enfants et a déposé un rapport de police, et les deux [ont passé sur CNN](https://www.cnn.com/2023/11/04/us/new-jersey-high-school-deepfake-porn/index.html) pour discuter de l'incident. La réponse de l’école pourrait, à terme, être considérée comme l’une des premières études de cas majeures en matière de réponse à ces incidents. À peu près au même moment que Westfield, le lycée Issaquah, dans l'État de Washington, était confronté à [son propre problème similaire](https://incidentdatabase.ai/cite/617) avec des deepfakes. + +La plupart des exemples examinés ci-dessus semblent principalement impliquer l’appropriation illicite de véritables portraits d’enfants. En revanche, le cas sud-coréen est un exemple de technologie utilisée pour générer des médias audiovisuels synthétiques représentant des enfants qui n’existent pas dans la vie réelle, ce qui présente également des problèmes importants. + +Récemment, [un incident](https://incidentdatabase.ai/cite/576/) a été révélé lorsqu'un utilisateur de LinkedIn a signalé qu'une application d'IA connue sous le nom de PicSo_ai générait un contenu inapproprié, de manière alarmante, en mettant l'accent sur les « filles ». Il ne s’agissait pas d’un cas isolé. Une recherche sur Instagram a recommandé des suggestions automatiques troublantes liées à des images de filles mineures générées par l’IA, marquées comme « populaires ». Cette découverte indique une tendance inquiétante selon laquelle l'IA est exploitée dans une zone grise et où, selon [une enquête récente du *Wall Street Journal*](https://www.wsj.com/tech/meta-instagram-video-algorithm-children-adult-sexual-content-72874155?mod=hp_lead_pos7), l'algorithme Reels d'Instagram recommande aux utilisateurs qui suivent de jeunes influenceurs des contenus sexuellement suggestifs et explicites liés aux enfants et aux adultes. Cela a soulevé d’importantes inquiétudes quant à la modération et à la sécurité du contenu de la plateforme, les publicités des grandes marques apparaissant à côté de ce contenu. Malgré les efforts de Meta pour mettre en œuvre des outils de sécurité, les défis en matière de curation de contenu algorithmique et de sécurité numérique sur les réseaux sociaux restent persistants. + +*Le Wall Street Journal* a également [rapporté récemment](https://www.wsj.com/tech/facebook-and-instagram-steer-predators-to-children-new-mexico-attorney-general-alleges-in-lawsuit-b76a5b04?mod=Searchresults_pos1&page=1) sur le procureur général du Nouveau-Mexique qui a déposé une plainte contre Meta, alléguant que les algorithmes de Facebook et d'Instagram ont dirigé les prédateurs et le contenu pornographique vers des comptes tests à thème mineur. L’enquête portait sur des images d’enfants fictifs générées par l’IA, ce qui a permis aux comptes de recevoir des messages explicites et des propositions sexuelles. Le procès affirme que les plateformes de Meta sont devenues un marché pour les prédateurs et critique leur incapacité à protéger les utilisateurs mineurs, citant plusieurs cas criminels d’exploitation via ces plateformes. Dans ce cas, ce sont les enquêteurs qui ont généré les images des mineurs fictifs, en adaptant les nouvelles technologies aux anciennes techniques de ce domaine spécifique de l'application de la loi. + +Une [étude récente](https://stacks.stanford.edu/file/druid:kh752sm9123/ml_training_data_csam_report-2023-12-21.pdf) de David Thiel de l'Observatoire Internet de Stanford détaille la présence de CSAM dans les données de formation de modèles d'apprentissage automatique génératifs, en se concentrant sur l'ensemble de données LAION-5B utilisé pour des modèles tels que Stable Diffusion. Grâce à diverses méthodes, notamment les classificateurs PhotoDNA et ML, Thiel a identifié de nombreuses instances connues et nouvelles de CSAM dans l'ensemble de données. Les résultats arrivent à point nommé car ils mettent en évidence la nécessité d'une conservation des données plus rigoureuse et de pratiques de formation modèles pour empêcher la perpétuation de contenus préjudiciables, conformément aux préoccupations soulevées par les incidents sur les plateformes de médias sociaux et soulignant l'importance d'un développement plus responsable de l'IA dans ce domaine. devant. + +Actuellement, les efforts juridiques visant à lutter contre les deepfakes CSAM ont été réactifs et fragmentaires. Le décret du président Biden sur l’IA vise cependant à établir des normes strictes pour empêcher toute utilisation abusive de l’IA, en se concentrant sur la sécurité nationale et la sécurité individuelle, ce qui implique l’authentification du contenu numérique et l’étiquetage des médias synthétiques, en particulier pour protéger les enfants des dommages causés par l’IA. Les développeurs doivent partager les résultats des tests de sécurité de l’IA avant leur utilisation publique, en ciblant des problèmes tels que la création de CSAM. L’ordonnance ordonne l’élaboration de normes pour l’authentification du contenu et la détection de l’IA, et traite du CSAM généré par l’IA et des images sexualisées non consensuelles. Avant le décret, [U.S. les procureurs généraux ont exhorté le Congrès](https://www.scag.gov/media/pvehppkm/54-state-ags-urge-study-of-ai-and-harmful-impacts-on-children.pdf) à enquêter sur le rôle de l'IA dans l'exploitation des enfants, soulignant la nécessité d'une législation complète sur la confidentialité des données. + +Pour l'instant, [aucun projet de loi fédéral global](https://www.nbcnews.com/news/us-news/little-recourse-teens-girls-victimized-ai-deepfake-nudes-rcna126399) n'existe, mais des efforts ont été déployés. été faites (par exemple, [H.R.3230 DEEP FAKES Accountability Act](https://www.congress.gov/bill/116th-congress/house-bill/3230)). Vous trouverez ci-dessous quatre exemples de législation au niveau des États : + +* [Californie, AB 602, Section 1708.86](https://pluralpolicy.com/app/legislative-tracking/bill/details/state-ca-20192020-ab602/202869) : modifie la loi existante pour permettre non seulement des mesures contre ceux qui créent et partagent du matériel sexuellement explicite sans consentement, mais met également l'accent spécifiquement sur les poursuites judiciaires contre les individus qui distribuent du matériel qu'ils n'ont pas créé, à condition qu'ils sachent que la personne représentée n'a pas consenti à sa création. Cet aspect du projet de loi est important car il étend la responsabilité à ceux qui propagent du contenu explicite non consensuel, quel que soit leur rôle dans sa création originale. + +* [Floride, CS/CS/SB 1798](https://www.flsenate.gov/Session/Bill/2022/1798/?Tab=BillText) : traite des infractions sexuelles impliquant des images modifiées, en particulier des deepfakes. Il criminalise la promotion de représentations sexuelles modifiées sans consentement, les définissant comme des crimes au troisième degré. Le projet de loi élargit la définition de « pornographie juvénile » pour inclure les images modifiées numériquement représentant des mineurs en train de se livrer à des relations sexuelles. Il augmente également les sanctions en cas de cyberharcèlement sexuel et de contact sexuel avec des animaux, y compris la possession de poupées sexuelles ressemblant à des enfants. Des exemptions sont accordées à certaines entités, comme les médias d'information et les forces de l'ordre, dans des conditions spécifiées. Le projet de loi met l’accent sur la protection des mineurs et sur la lutte contre les tendances émergentes en matière d’exploitation sexuelle numérique. + +* [Illinois, House Bill 2123](https://legiscan.com/IL/bill/HB2123/2023) : Redéfinit « l'image sexuelle » dans la Loi sur les recours civils en cas de diffusion non consensuelle d'images sexuelles privées pour inclure des images montrant de la nudité ou des images sexuelles. conduite, qu’elle soit réelle ou modifiée numériquement. Il permet aux personnes figurant sur ces images d’intenter des poursuites judiciaires contre ceux qui partagent ou menacent de partager ces images, notamment en l’absence de consentement. L’amendement précise que la divulgation de la modification numérique d’une image ne constitue pas une défense. Il supprime également certaines responsabilités des services informatiques interactifs et précise que l’ajout d’un message politique à de telles images n’en fait pas un sujet d’intérêt public. De plus, les tribunaux peuvent ordonner l’arrêt de la diffusion de ces images. + +* [Virginia, Code 18.2-386.2](https://law.lis.virginia.gov/vacode/title18.2/chapter8/section18.2-386.2/) : le partage ou la vente malveillante de nu constitue un délit de classe 1. ou des images sexuellement explicites de quelqu'un sans son consentement, surtout si elles sont faites pour harceler, contraindre ou intimider. Cela inclut les images qui ont été modifiées numériquement pour représenter une personne. Les fournisseurs de services Internet ne sont pas tenus responsables du contenu partagé par d'autres. Une action en justice peut être intentée là où l'acte illicite a eu lieu ou là où l'image a été manipulée. D'autres frais juridiques peuvent également s'appliquer. + +Dans la base de données des incidents AI (AIID), nous avons catalogué et étudié avec inquiétude ces récents incidents CSAM. Comme toute technologie, l’IA générative présente des risques et des opportunités – et les risques pour les enfants dans ce cas sont sérieux. Si vous souhaitez nous rejoindre dans notre mission de documentation des incidents d'IA dans le but d'apprendre des erreurs passées pour atténuer les risques futurs, vous pouvez nous contacter via notre page [contact](https://incidentdatabase.ai/contact/) . Nous acceptons les soumissions signalant tout incident d'IA en utilisant notre page [submission](https://incidentdatabase.ai/apps/submit/) ; Cependant, veuillez noter que même si nous suivons et analysons activement les tendances et les incidents CSAM, l'AIID n'est pas la destination directe des rapports pour les CSAM réels. Veuillez adresser le signalement des abus sexuels sur les enfants au ministère de la Justice [Section contre l'exploitation des enfants et l'obscénité](https://www.justice.gov/criminal/criminal-ceos/report-violations). \ No newline at end of file diff --git a/site/gatsby-site/blog/deepfakes-child-safety/index.mdx b/site/gatsby-site/blog/deepfakes-child-safety/index.mdx new file mode 100644 index 0000000000..e8cad11d2b --- /dev/null +++ b/site/gatsby-site/blog/deepfakes-child-safety/index.mdx @@ -0,0 +1,53 @@ +--- +title: 'Deepfakes and Child Safety: A Survey and Analysis of 2023 Incidents and Responses' +metaTitle: 'Deepfakes and Child Safety: A Survey and Analysis of 2023 Incidents and Responses' +metaDescription: "" +date: '2024-01-09' +image: './images/image_doj.jpg' +author: 'Daniel Atherton' +slug: '/blog/deepfakes-and-child-safety' +--- + +**DISCLAIMER:** This post is not legal advice or commentary and should not be construed as such. + +2023 saw an increase in AI-generated child sex abuse materials (CSAM), along with prosecutions of offenders, a variety of legislative attempts to combat AI deepfakes targeting minors, and the Biden administration's [executive order on artificial intelligence](https://www.whitehouse.gov/briefing-room/presidential-actions/2023/10/30/executive-order-on-the-safe-secure-and-trustworthy-development-and-use-of-artificial-intelligence/). + +Deepfakes can be broadly classified into two main categories, each with its own subset related to CSAM. The first category includes deepfakes of actual individuals, where the predominant harm associated with CSAM arises from the generation of deepfake pornography featuring real children. The second category encompasses deepfakes where the subjects are entirely virtual yet convincingly realistic. In this category, CSAM concerns are primarily linked to the creation of inappropriate synthetic audiovisual media depicting virtual children. Collectively, these two taxonomic categories demonstrate the various troubling ways deepfake technology can be employed, especially in the generation and proliferation of CSAM. + +This article provides a snapshot of some of the work of the AI Incident Database in tracking these emerging incidents, along with a survey of some of the nascent legislative responses. + +Three specific court cases—one from South Korea, one from Quebec, and one from North Carolina—center on men who used AI to generate deepfake pornography of children. + +In [South Korea](https://incidentdatabase.ai/cite/600), the unnamed defendant generated 360 images of children in sexual situations. During the trial, prosecutors emphasized that the legal definition of sexually exploitative material should extend to depictions involving “virtual humans” and not be limited to images of real children. They argued that with the advent of “high level” technology, such as AI, imagery realistic enough to resemble actual minors can constitute sexually abusive content. This stance was supported by the ruling in the case, aligning with the prosecutor’s viewpoint on modern technological capabilities and their implications in law. + +In the [Quebec case](https://incidentdatabase.ai/cite/604), Steven Larouche from Sherbrooke was sentenced to over three years in prison for producing synthetic child pornography using deepfake technology. He admitted to creating at least seven videos by superimposing faces onto other bodies. Additionally, Larouche was convicted of possessing a vast collection of child pornography, leading to an extra four and a half years in prison. Provincial court judge Benoit Gagnon noted this as the country's first deepfake-related child exploitation case, expressing concerns about the potential misuse of this technology to manipulate social media images of children. + +The [North Carolina case](https://incidentdatabase.ai/cite/605) involved the child psychiatrist David Tatum. Among his other charges, evidence presented at his trial revealed that Tatum employed AI to transform clothed images of minors into explicit material, including altering photos from a school dance and a first-day-of-school celebration using a web-based AI application. + +Analyzing the media discourse yields a divergence in the kinds of incidents emerging as a result of this technology. The above cases align with longstanding reporting on child predators and their sentencing, albeit with the emerging presence of AI as part of the overall crime profile. Below, two major cases of a different kind demonstrate the proliferation in local communities (whether towns or schools) of girls’ likenesses being used in deepfake pornography. + +In [Almendralejo, Spain](https://incidentdatabase.ai/cite/610), AI was used to create and distribute images of young girls in which they were made to appear naked. The images, involving several local girls, were reportedly shared widely, leading to a police probe. Mothers of the girls raised awareness about the issue, concerned about the potential to upload these images to pornographic sites. Regional officials confirmed an ongoing investigation, with some suspects identified. This incident, described as gender-based digital violence, drew widespread condemnation. + +Similarly, at [Westfield High School in New Jersey](https://incidentdatabase.ai/cite/597), AI-generated pornographic images of students caused significant distress and sparked an investigation. Dorota Mani, whose daughter Francesca was targeted, expressed her concerns about the impact of AI on children and filed a police report, and the two of them [went on CNN](https://www.cnn.com/2023/11/04/us/new-jersey-high-school-deepfake-porn/index.html) to discuss the incident. The school’s response may in time be viewed as one of the first major case studies for responding to these incidents. Around the same time as Westfield, Issaquah High School in Washington State was experiencing [its own similar problem](https://incidentdatabase.ai/cite/617) with deepfakes. + +Most of the examples surveyed above seem primarily to involve the misappropriation of real children’s likenesses. In contrast, the South Korean case is an example of technology used to generate synthetic audiovisual media depicting children who do not exist in real life, which also presents significant problems. + +Recently, [an incident](https://incidentdatabase.ai/cite/576/) came to light when a LinkedIn user reported that an AI app known as PicSo_ai was generating inappropriate content, alarmingly, with a focus on “girls.” This was not a standalone case. A search on Instagram recommended troubling auto-suggestions related to AI-generated images of underage girls, marked as “popular.” This discovery points toward a concerning pattern where AI is being exploited in a gray zone, and where, according to [a recent *Wall Street Journal* investigation](https://www.wsj.com/tech/meta-instagram-video-algorithm-children-adult-sexual-content-72874155?mod=hp_lead_pos7), Instagram’s Reels algorithm recommended sexually suggestive and explicit content related to children and adults to users following young influencers. This raised significant concerns about the platform’s content moderation and safety, with major brands’ ads appearing alongside such content. Despite Meta’s efforts to implement safety tools, challenges in algorithmic content curation and digital safety on social media are persistent. + +*The Wall Street Journal* also [recently reported](https://www.wsj.com/tech/facebook-and-instagram-steer-predators-to-children-new-mexico-attorney-general-alleges-in-lawsuit-b76a5b04?mod=Searchresults_pos1&page=1) on the New Mexico Attorney General filing a lawsuit against Meta, alleging Facebook and Instagram algorithms steered predators and pornographic content to minor-themed test accounts. The investigation involved AI-generated images of fictional children, resulting in the accounts receiving explicit messages and sexual propositions. The lawsuit claims Meta’s platforms have become a marketplace for predators and criticizes their failure to protect underage users, citing several criminal cases of exploitation via these platforms. In this case, it was the investigators who generated the images of the fictional minors, adapting new technology to old techniques in this specific field of law enforcement. + +A [recent study](https://stacks.stanford.edu/file/druid:kh752sm9123/ml_training_data_csam_report-2023-12-21.pdf) by David Thiel of the Stanford Internet Observatory details the presence of CSAM in the training data of generative machine learning models, focusing on the LAION-5B dataset used for models like Stable Diffusion. Through various methods, including PhotoDNA and ML classifiers, Thiel identified numerous known and new instances of CSAM in the dataset. The findings are timely as they showcase the need for more rigorous data curation and model training practices to prevent the perpetuation of harmful content, in line with the concerns raised by the incidents on social media platforms and emphasizing the importance of further responsible AI development on this front. + +Currently, legal efforts to address CSAM deepfakes have been reactive and piecemeal. President Biden’s executive order on AI, though, aims to establish strict standards to prevent AI misuse, focusing on national security and individual safety, which entails authenticating digital content and labeling synthetic media, especially to protect children from AI-enabled harm. Developers must share AI safety test results before public use, targeting issues like CSAM creation. The order directs developing standards for content authentication and AI detection, and addresses AI-generated CSAM and non-consensual sexualized images. Preceding the executive order, [U.S. attorneys general urged Congress](https://www.scag.gov/media/pvehppkm/54-state-ags-urge-study-of-ai-and-harmful-impacts-on-children.pdf) to investigate AI’s role in child exploitation, emphasizing the need for comprehensive data privacy legislation. + +As of yet, [no overarching federal bill](https://www.nbcnews.com/news/us-news/little-recourse-teens-girls-victimized-ai-deepfake-nudes-rcna126399) exists, but efforts have been made (e.g., [H.R.3230 DEEP FAKES Accountability Act](https://www.congress.gov/bill/116th-congress/house-bill/3230)). Below are four examples of state-level legislation: + +* [California, AB 602, Section 1708.86](https://pluralpolicy.com/app/legislative-tracking/bill/details/state-ca-20192020-ab602/202869): Modifies existing law to not only allow action against those who create and share sexually explicit material without consent but also specifically emphasizes legal action against individuals who distribute such material they did not create, provided they are aware that the depicted person did not consent to its creation. This facet of the bill is important as it extends responsibility to those who propagate non-consensual explicit content, regardless of their role in its original creation. + +* [Florida, CS/CS/SB 1798](https://www.flsenate.gov/Session/Bill/2022/1798/?Tab=BillText): Addresses sexual offenses involving altered images, specifically deepfakes. It criminalizes the promotion of altered sexual depictions without consent, defining these as third-degree felonies. The bill expands the definition of “child pornography” to include digitally modified images depicting minors in sexual conduct. It also increases penalties for sexual cyberharassment and sexual contact with animals, including the possession of child-like sex dolls. Exemptions are made for certain entities, like news media and law enforcement, under specified conditions. The bill emphasizes protecting minors and addressing emerging digital sexual exploitation trends. + +* [Illinois, House Bill 2123](https://legiscan.com/IL/bill/HB2123/2023): Redefines “sexual image” in the Civil Remedies for Nonconsensual Dissemination of Private Sexual Images Act to include images showing nudity or sexual conduct, whether real or digitally altered. It allows individuals in these images to take legal action against those who share or threaten to share these images, particularly when there’s no consent. The amendment specifies that disclosing an image’s digital alteration isn’t a defense. It also removes certain liabilities from interactive computer services and clarifies that adding a political message to such images doesn’t make them a matter of public concern. Additionally, courts can grant orders to stop the dissemination of these images. + +* [Virginia, Code 18.2-386.2](https://law.lis.virginia.gov/vacode/title18.2/chapter8/section18.2-386.2/): Makes it a Class 1 misdemeanor to maliciously share or sell nude or sexually explicit images of someone without their consent, especially if done to harass, coerce, or intimidate. This includes images that have been digitally altered to depict a person. Internet service providers are not held responsible for the content shared by others. Legal action can be pursued where the unlawful act took place or where the image was handled. Other legal charges may also apply. + +At the AI Incident Database (AIID), we have been cataloging and researching these recent CSAM incidents with concern. Like all technology, generative AI poses risks and opportunities—and the risks to children in this case are serious. If you would like to join us in our mission of documenting AI incidents with the goal of learning from past mistakes to mitigate future risks, you can connect with us through our [contact](https://incidentdatabase.ai/contact/) page. We welcome submissions reporting any and all AI incidents using our [submission](https://incidentdatabase.ai/apps/submit/) page; however, please be advised that while we actively track and analyze CSAM trends and incidents, the AIID is not the direct reporting destination for actual CSAM. Please direct the reporting of CSAM itself to the Department of Justice [Child Exploitation and Obscenity Section](https://www.justice.gov/criminal/criminal-ceos/report-violations). \ No newline at end of file diff --git a/site/gatsby-site/config.js b/site/gatsby-site/config.js index afe216b212..d63f01a0db 100644 --- a/site/gatsby-site/config.js +++ b/site/gatsby-site/config.js @@ -201,6 +201,16 @@ const config = { rollbar: { token: process.env.GATSBY_ROLLBAR_TOKEN, }, + discover: { + taxa: ['CSETv0', 'CSETv1', 'GMF'], + }, + cloudflareR2: { + accountId: process.env.CLOUDFLARE_R2_ACCOUNT_ID, + accessKeyId: process.env.CLOUDFLARE_R2_ACCESS_KEY_ID, + secretAccessKey: process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY, + bucketName: process.env.CLOUDFLARE_R2_BUCKET_NAME, + publicBucketUrl: process.env.GATSBY_CLOUDFLARE_R2_PUBLIC_BUCKET_URL, + }, }; module.exports = config; diff --git a/site/gatsby-site/content/about/index.es.mdx b/site/gatsby-site/content/about/index.es.mdx index 0122fbb60f..969ccc6923 100644 --- a/site/gatsby-site/content/about/index.es.mdx +++ b/site/gatsby-site/content/about/index.es.mdx @@ -9,94 +9,113 @@ aiTranslated: true import Leaderboards from '../../src/components/landing/Leaderboards'; import Sponsors from '../../src/components/landing/Sponsors'; -## ¿Por qué "Incidentes de IA"? +## ¿Por qué "incidentes de IA"? -Actualmente, los sistemas inteligentes son propensos a fallas imprevistas y, a menudo, peligrosas cuando se implementan en el mundo real. Al igual que el sector del transporte anterior (por ejemplo, [FAA](https://www.faa.gov/data_research/accident_incident/) y [FARS](https://www.nhtsa.gov/research-data/fatality-analysis-reporting-system-fars) y más recientemente [sistemas informáticos](https://cve.mitre.org/cve/), los sistemas inteligentes requieren un repositorio de problemas experimentados en el mundo real para que los futuros investigadores y desarrolladores puedan mitigar o evitar la repetición de malos resultados. +Actualmente, los sistemas inteligentes son propensos a sufrir fallos imprevistos y, a menudo, peligrosos cuando se implementan en el mundo real. Al igual que el sector del transporte anterior (por ejemplo, [FAA](https://www.faa.gov/data_research/accident_incident/) y [FARS](https://www.nhtsa.gov/research-data/fatality-Analysis-reporting-system-fars)) y más recientemente [sistemas informáticos](https://cve.mitre.org/cve/), los sistemas inteligentes requieren un repositorio de problemas experimentados en el mundo real para que los futuros investigadores y desarrolladores puedan mitigar o evitar malos resultados repetidos. ## ¿Qué es un incidente? -El conjunto inicial de más de 1000 informes de incidentes ha sido intencionalmente de naturaleza amplia. Los ejemplos actuales incluyen, +El conjunto inicial de más de 1.000 informes de incidentes ha sido intencionalmente de naturaleza amplia. Los ejemplos actuales incluyen, * Un *coche autónomo* [mata a un peatón](/cite/4) -* Un *algoritmo de negociación* provoca un mercado "[flash crash](/cite/28)" donde se transfieren miles de millones de dólares entre las partes -* Un *sistema de reconocimiento facial* [hace que una persona inocente sea arrestada](/cite/74) +* Un *algoritmo comercial* provoca una "[caída repentina](/cite/28)" en el mercado donde se transfieren miles de millones de dólares entre las partes. +* Un *sistema de reconocimiento facial* [provoca que una persona inocente sea arrestada](/cite/74) -Lo invitamos a [explorar los incidentes recopilados hasta la fecha](/apps/discover), [ver la lista completa](/summaries/incidents) y [enviar](/apps/submit) informes de incidentes adicionales. Se invita a los investigadores a revisar nuestra [definición de trabajo de incidentes de IA](/research/1-criteria). +Está invitado a [explorar los incidentes recopilados hasta la fecha](/apps/discover), [ver la lista completa](/summaries/incidents) y [enviar](/apps/submit) informes de incidentes adicionales. Se invita a los investigadores a revisar nuestra [definición funcional de incidentes de IA](/research/1-criteria). ## Usuarios actuales y futuros -La base de datos es un [producto de datos](/develop) y [una colección de aplicaciones](/apps) [en constante evolución](/research/2-roadmap). +La base de datos es un producto de datos y una colección de aplicaciones en constante evolución. -* **Usuarios actuales** incluye arquitectos de sistemas, desarrolladores de productos industriales, gerentes de relaciones públicas, [investigadores](/research) e investigadores de políticas públicas. Se invita a estos usuarios a usar la aplicación [Discover](/apps/discover) para descubrir de manera proactiva cómo los sistemas inteligentes implementados recientemente han producido resultados inesperados en el mundo real. Al hacerlo, pueden evitar cometer errores similares en su desarrollo. -* **Los usos futuros** [evolucionarán](/research/2-roadmap) a través de las contribuciones de código de la comunidad [de código abierto](https://github.com/PartnershipOnAI/aiid), incluida la base de datos adicional [resúmenes](/apps/summaries) y [taxonomías](/research/2-roadmap). +* **Los usuarios actuales** incluyen arquitectos de sistemas, desarrolladores de productos industriales, gerentes de relaciones públicas, [investigadores](/investigación) e investigadores de políticas públicas. Se invita a estos usuarios a utilizar la aplicación [Discover](/apps/discover) para descubrir de forma proactiva cómo los sistemas inteligentes recientemente implementados han producido resultados inesperados en el mundo real. Al hacerlo, podrán evitar cometer errores similares en su desarrollo. +* **Usos futuros** [evolucionarán](/research/2-roadmap) a través de las contribuciones de código de la comunidad [código abierto](https://github.com/responsible-ai-collaborative/aiid), incluidos [resúmenes](/about_apps/) y [taxonomías](/research/2-roadmap) de bases de datos. -## ¿Cuándo debe informar un incidente? +## ¿Cuándo debería informar un incidente? -Cuando tenga dudas sobre si un evento califica como incidente, [envíelo](/apps/submit). Este proyecto pretende converger en una [definición compartida](/research/1-criteria) de "Incidente de IA" a través de la exploración de los incidentes candidatos presentados por la comunidad en general. +Si tiene dudas sobre si un evento califica como incidente, [envíelo](/apps/submit). Este proyecto pretende converger en una [definición compartida](/research/1-criteria) de "Incidente de IA" mediante la exploración de los incidentes candidatos presentados por la comunidad en general. ## Junta Directiva -La base de datos de incidentes es administrada de manera participativa por personas y organizaciones que aportan código, investigación e impactos más amplios. Si desea participar en la gobernanza del proyecto, [contactenos](/contact) con nosotros e incluya su contribución prevista a la base de datos de incidentes de AI. +La base de datos de incidentes es administrada de manera participativa por personas y organizaciones que contribuyen con código, investigación e impactos más amplios. Si desea participar en la gobernanza del proyecto, [póngase en contacto](/contact) con nosotros e incluya su contribución prevista a la base de datos de incidentes de IA. **Miembros votantes** -* **[Helen Toner](https://cset.georgetown.edu/staff/helen-toner/):** Helen Toner es Directora de Estrategia en el Center for Security and Emerging Technology (CSET) de Georgetown. Anteriormente, trabajó como analista de investigación sénior en Open Philanthropy, donde asesoró a legisladores y donantes sobre políticas y estrategias de IA. Entre trabajar en Open Philanthropy y unirse a CSET, Helen vivió en Beijing y estudió el ecosistema chino de IA como investigadora afiliada del Centro para la Gobernanza de la IA de la Universidad de Oxford. Helen ha escrito para Foreign Affairs y otros medios sobre las implicaciones de seguridad nacional de la IA y el aprendizaje automático para China y los Estados Unidos, además de testificar ante la Comisión de Revisión Económica y de Seguridad de EE. UU.-China. Es miembro de la junta directiva de OpenAI. Helen tiene una Maestría en Estudios de Seguridad de Georgetown, así como una Licenciatura en Ingeniería Química y un Diplomado en Idiomas de la Universidad de Melbourne. -**Contribuciones:** [Investigación de incidentes de IA](https://cset.georgetown.edu/publication/ai-accidents-an-emerging-threat/) y supervisión de la [taxonomía CSET](https://incidentdatabase.ai/taxonomía/cset). +* **[Patrick Hall](https://business.gwu.edu/johnston-patrick-hall):** Patrick fue recientemente científico principal en bnh.ai, una firma de abogados con sede en D.C. que se especializa en inteligencia artificial y análisis de datos. Patrick también es profesor asistente en la Escuela de Negocios de la Universidad George Washington. Antes de cofundar bnh.ai, Patrick dirigió esfuerzos responsables de IA en la empresa de software de aprendizaje automático H2O.ai, donde su trabajo dio como resultado una de las primeras soluciones comerciales del mundo para un aprendizaje automático explicable y justo. Entre otros escritos sobre medios académicos y tecnológicos, Patrick es el autor principal de libros electrónicos populares sobre aprendizaje automático explicable y responsable. Patrick estudió química computacional en la Universidad de Illinois antes de graduarse en el Instituto de Análisis Avanzado de la Universidad Estatal de Carolina del Norte. +**Contribuciones:** Patrick es un colaborador [principal](https://incidentdatabase.ai/summaries/leaderboard) de informes de incidentes para la base de datos de incidentes de IA y proporciona liderazgo estratégico para la junta. -* **[Patrick Hall](https://business.gwu.edu/johnston-patrick-hall):** Patrick es científico principal en bnh.ai, una firma de abogados con sede en D.C. que se especializa en IA y análisis de datos. Patrick también se desempeña como profesor visitante en la Escuela de Negocios de la Universidad George Washington. Antes de cofundar bnh.ai, Patrick lideró los esfuerzos de inteligencia artificial responsable en la firma de software de aprendizaje automático H2O.ai, donde su trabajo resultó en una de las primeras soluciones comerciales del mundo para el aprendizaje automático explicable y justo. Entre otros escritos académicos y de medios tecnológicos, Patrick es el autor principal de libros electrónicos populares sobre aprendizaje automático explicable y responsable. Patrick estudió química computacional en la Universidad de Illinois antes de graduarse del Instituto de Analítica Avanzada de la Universidad Estatal de Carolina del Norte. -**Contribuciones:** Patrick es el colaborador [principal](https://incidentdatabase.ai/summaries/leaderboard) de informes de incidentes para el Proyecto de base de datos de incidentes de AI. +* **[Heather Frase](https://cset.georgetown.edu/staff/heather-frase/):** Heather Frase, PhD es investigadora principal del [Centro de Seguridad y Tecnología Emergente de Georgetown](https://cset.georgetown.edu/) (CSET), donde trabaja en Evaluación de IA. También se desempeña como asesora no remunerada del proyecto Open Loop de Meta, brindando experiencia en la implementación del Marco de Gestión de Riesgos de IA del Instituto Nacional de Estándares y Tecnología. Antes de unirse a CSET, Heather pasó ocho años brindando soporte de análisis de datos, modelado computacional, aprendizaje automático (ML) e inteligencia artificial (AI) para contratos federales, de inteligencia y de defensa. Además, Heather pasó 14 años en el Instituto de Análisis de Defensa (IDA), como directora de apoyo de Pruebas y Evaluación Operativas (DOT&E). En IDA dirigió equipos de investigación analítica para aplicar experiencia científica, tecnológica y estadística para desarrollar métricas de datos y planes de recopilación para pruebas operativas de los principales sistemas de defensa, analizar datos de pruebas y producir evaluaciones de efectividad e idoneidad operativas. Tiene un doctorado. en Ciencias de Materiales del Instituto de Tecnología de California y una licenciatura en Física de la Universidad de Miami en Oxford Ohio. +**Contribuciones:** Heather desarrolla investigaciones de incidentes de IA además de su supervisión de la [taxonomía CSET](/taxonomy/cset). -* **[Sean McGregor](https://seanbmcgregor.com/):** Sean McGregor fundó el proyecto de la base de datos de incidentes de IA y recientemente dejó un puesto como arquitecto de aprendizaje automático en la startup del acelerador neuronal Syntiant para poder concentrarse en la seguridad de sistemas inteligentes a tiempo completo. El trabajo del Dr. McGregor abarca aceleradores neuronales para la inferencia de eficiencia energética, aprendizaje profundo para el habla y la heliofísica, y aprendizaje de refuerzo para la política de supresión de incendios forestales. Fuera de su trabajo remunerado, Sean organizó una serie de talleres en las principales conferencias académicas de IA sobre el tema "AI for Good" y actualmente está desarrollando un enfoque basado en incentivos para hacer que la IA sea más segura a través de auditorías y seguros. **Contribuciones:** Sean es voluntario como mantenedor de proyectos y editor del proyecto Base de datos de incidentes de IA (AIID). +* **[Kristian J. Hammond](https://www.mccormick.northwestern.edu/research-faculty/directory/profiles/hammond-kristian.html):** Kris Hammond es el profesor de informática Bill y Cathy Osborn Science en la Universidad Northwestern y cofundador de la empresa de Inteligencia Artificial Narrative Science, recientemente adquirida por Salesforce. También es el líder docente de la iniciativa CS + X de Northwestern, que explora cómo se puede utilizar el pensamiento computacional para transformar campos como el derecho, la medicina, la educación y los negocios. Es director de la Maestría en Ciencias en Inteligencia Artificial (MSAI) de Northwestern. Más recientemente, el Dr. Hammond fundó el Centro para el avance de la seguridad en la inteligencia de las máquinas (CASMI), un centro de investigación financiado por Underwriter's Laboratories. CASMI se centra en hacer operativo el diseño y la evaluación de sistemas de IA desde la perspectiva de su impacto en la vida y la seguridad humana. +**Contribuciones:** Kris está desarrollando un proyecto colaborativo centrado en estudios de casos de incidentes. -**Miembros sin derecho a voto** +**Junta Emérita** -* **[Neama Dadkhahnikoo](https://www.linkedin.com/in/neama/):** Neama Dadkhahnikoo es experto en inteligencia artificial y emprendimiento, con más de 15 años de experiencia en desarrollo de tecnología en startups, no beneficios y grandes empresas. Actualmente se desempeña como gerente de productos para Vertex AI, la plataforma unificada de Google Cloud para el aprendizaje automático de extremo a extremo. Anteriormente, el Sr. Dadkhahnikoo fue director de IA y operaciones de datos en la Fundación XPRIZE, CTO de CaregiversDirect (startup de IA para atención domiciliaria), cofundador y director de operaciones de Textpert (startup de IA para salud mental) y consultor de startup. Comenzó su carrera como desarrollador de software para The Boeing Company. El Sr. Dadkhahnikoo tiene un MBA de UCLA Anderson; una Maestría en Gestión de Proyectos del Stevens Institute of Technology; y una licenciatura en Matemáticas Aplicadas y Ciencias de la Computación, con especialización en Física, de UC Berkeley. -**Contribuciones:** Neama se ofrece como voluntario para mantener organizada la Responsible AI Collaborative fuera de su rol como gerente de producto en Google. -* **[Kit Harris](https://www.linkedin.com/in/kitharris/):** Kit dirige investigaciones de subvenciones e investiga campos prometedores en Longview Philanthropy. También escribe sobre las recomendaciones e investigaciones de Longview para clientes filántropos de Longview. Antes de centrarse en el trabajo filantrópico de alto impacto, Kit trabajó como comerciante de derivados de crédito con J.P. Morgan. Durante ese tiempo, donó la mayoría de sus ingresos a organizaciones benéficas de alto impacto. Kit tiene un título de primera clase en matemáticas de la Universidad de Oxford. -**Contribuciones:** Kit sirve como observador de la junta, brinda asesoramiento estratégico y es el punto de contacto de RAIC en Longview. +Los miembros eméritos de la junta directiva son aquellos que se han distinguido particularmente en su servicio a Responsible AI Collaborative. No ocupan ningún puesto de gobierno dentro de la organización. +* **[Sean McGregor](https://seanbmcgregor.com/):** Sean McGregor fundó el proyecto AI Incident Database y recientemente se unió al [Digital Safety Research Institute](https://ul.org/research/digital-safety) como director fundador. Antes de iniciar Responsible AI Collaborative, Sean dejó un puesto como arquitecto de aprendizaje automático en la startup de acelerador neuronal Syntiant para poder centrarse en la garantía de sistemas inteligentes a tiempo completo. El trabajo del Dr. McGregor abarca aceleradores neuronales para inferencias energéticamente eficientes, aprendizaje profundo para el habla y heliofísica, y aprendizaje reforzado para políticas de extinción de incendios forestales. Además de su trabajo remunerado, Sean organizó una serie de talleres en importantes conferencias académicas sobre IA sobre el tema "IA para el bien" y ahora busca aprovechar de forma segura los beneficios de la IA conectando los registros de incidentes de IA con los programas de prueba de IA. + +* **[Helen Toner](https://cset.georgetown.edu/staff/helen-toner/):** Helen Toner es directora de estrategia en el Centro de Seguridad y Tecnología Emergente (CSET) de Georgetown. Anteriormente trabajó como analista de investigación senior en Open Philanthropy, donde asesoró a formuladores de políticas y donantes sobre políticas y estrategias de IA. Entre su trabajo en Open Philanthropy y su incorporación a CSET, Helen vivió en Beijing, estudiando el ecosistema chino de IA como investigadora afiliada del Centro para la Gobernanza de la IA de la Universidad de Oxford. Helen ha escrito para Asuntos Exteriores y otros medios sobre las implicaciones de seguridad nacional de la IA y el aprendizaje automático para China y Estados Unidos, además de testificar ante la Comisión de Revisión Económica y de Seguridad entre Estados Unidos y China. Es miembro de la junta directiva de OpenAI. Helen tiene una maestría en Estudios de Seguridad de Georgetown, así como una licenciatura en Ingeniería Química y un diploma en Idiomas de la Universidad de Melbourne. ## Colaboradores -**Contribuidores de código abierto:** personas que han contribuido con más de una solicitud de incorporación de cambios, gráficos, copias del sitio o informes de errores a la base de datos de incidentes de AI. +**Colaboración responsable de IA:** Personas que sirven a la organización detrás de la base de datos de incidentes de IA. -* [César Varela](https://github.com/cesarvarela) -* [Alex Musca](https://github.com/alexmcode) -* [Chloe Kam](http://kamchy.com) (Logotipos AIID) -* [JT McHorse](https://github.com/jt-mchorse) -* Seth Rey +* [Scott Allen Cambo](https://www.scottalencambo.com/) +* [Janet Boutilier Schwartz](https://www.linkedin.com/in/janet-boutilier-schwartz/) -**Asociación en miembros del personal de AI:** -[Jingying Yang](https://www.linkedin.com/in/jingyingyang/) y [Dr. Christine Custis](https://www.su.edu/symposium/business-symposium-speakers/christine-custis/) contribuyó significativamente a las primeras etapas del AIID. +**Instituto de Investigación de Seguridad Digital (DSRI):** Personas afiliadas a [DSRI](https://ul.org/research/digital-safety), que brinda apoyo sustancial al programa AIID. -**Editores de incidentes:** personas que resuelven los envíos de incidentes a la base de datos. +* [Kevin Paeth](https://www.linkedin.com/in/kevinpaeth/) es líder de DSRI +* [César Varela](https://github.com/cesarvarela) es Ingeniero Full Stack +* [Luna McNulty](https://lmcnulty.me/) es ingeniera de UX +* [Pablo Costa](https://www.linkedin.com/in/pablo-costa/) es ingeniero Full Stack +* [Clara Youdale Pinelli](https://www.linkedin.com/in/clarayoudale/) es ingeniera front-end +* [Sean McGregor](https://seanbmcgregor.com/) es director de DSRI +**Editores de incidentes:** Personas que resuelven los envíos de incidentes a la base de datos y los mantienen. + +* [Daniel Atherton](https://www.linkedin.com/in/daniel-atherton-167819251/) * [Sean McGregor](https://seanbmcgregor.com/) * [Khoa Lam](https://www.linkedin.com/in/khoalklam/) * [Kate Perkins](https://www.linkedin.com/in/kateeperkins/) * [Janet Boutilier Schwartz](https://www.linkedin.com/in/janet-boutilier-schwartz/) -* [Daniel Atherton](https://www.linkedin.com/in/daniel-atherton-167819251/) + +Además, [Zachary Arnold](https://cset.georgetown.edu/staff/zachary-arnold/) hizo contribuciones significativas a los [criterios del incidente](/research/1-criteria). **Editores de taxonomías:** Organizaciones o personas que han contribuido [taxonomías](https://incidentdatabase.ai/taxonomies) a la base de datos. -* [Center for Security and Emerging Technology](https://cset.georgetown.edu/) (CSET) +* [Centro de Seguridad y Tecnología Emergente](https://cset.georgetown.edu/) (CSET) +* [Nikiforos Pittaras](https://npit.github.io/) (GMF) + +**Partnership on AI:** + +* [Jingying Yang](https://www.linkedin.com/in/jingyingyang/) y [Dr. Christine Custis](https://www.su.edu/symposium/business-symposium-speakers/christine-custis/) contribuyó significativamente a las primeras etapas del AIID. + +**Colaboradores de código abierto:** Personas que han contribuido con más de una solicitud de extracción, gráficos, copia del sitio o informe de error a la base de datos de incidentes de IA. + +* [Neama Dadkhahnikoo](https://www.linkedin.com/in/neama/): Neama se desempeñó como directora ejecutiva voluntaria y observadora de la junta directiva de Responsible AI Collaborative. +* [Kit Harris](https://www.linkedin.com/in/kitharris/): Kit se desempeñó como observador de la junta y brindó asesoramiento estratégico desde su puesto como asesor de subvenciones. +* [Alex Muscă](https://github.com/alexmcode) +* [Chloe Kam](http://kamchy.com) Desarrolló el logotipo de AIID +* [JT McHorse](https://github.com/jt-mchorse) +* Seth Reid -**Contribuyentes de incidentes:** personas que han contribuido con una gran cantidad de incidentes a la base de datos. +**Contribuyentes de incidentes:** Personas que han contribuido con una gran cantidad de incidentes a la base de datos. -* [Kate Perkins (Intel)](/apps/discover?display=detalles&lang=en&page=1&submitters=Kate%20Perkins) -* [Roman Lutz (Instituto Max Planck para Intelligent Systems, anteriormente Microsoft)](/apps/discover?display=details&lang=en&page=1&submitters=Roman%20Lutz) +* [Roman Lutz (Instituto Max Planck de Sistemas Inteligentes, anteriormente Microsoft)](/apps/discover?display=details&lang=en&page=1&submitters=Roman%20Lutz) * [Patrick Hall (Burt and Hall LLP)](/apps/discover?display=details&lang=en&page=1&submitters=Patrick%20Hall%20%28BNH.ai%29) -* [Catherine Olsson (Google)](/apps/discover?display=detalles&lang=en&page=1&submitters=Catherine%20Olsson) -* [Roman Yampolskiy (Universidad de Louisville)](/apps/discover?display=detalles&lang=en&page=1&submitters=Roman%20Yampolskiy) -* Sam Yoon (como contratista de PAI, luego con Deloitte Consulting, luego con la Kennedy School of Government) +* [Catherine Olsson (Google)](/apps/discover?display=details&lang=en&page=1&submitters=Catherine%20Olsson) +* [Roman Yampolskiy (Universidad de Louisville)](/apps/discover?display=details&lang=en&page=1&submitters=Roman%20Yampolskiy) +* Sam Yoon (como contratista de PAI, luego de Deloitte Consulting, luego de la Kennedy School of Government) -Las siguientes personas han recopilado una gran cantidad de incidentes que están pendientes de ingesta. +Las siguientes personas han recogido un gran número de incidencias que están pendientes de ingestión. -* Zachary Arnold, Helen Toner, Ingrid Dickinson, Thomas Giallella y Nicolina Demakos (Center for Security and Emerging Technology, Georgetown) -* Charlie Pownall a través de IA, repositorio de controversias e incidentes algorítmicos y de automatización [(AIAAIC)](https://www.aiaaic.org/) +* Zachary Arnold, Helen Toner, Ingrid Dickinson, Thomas Giallella y Nicolina Demakos (Centro de Seguridad y Tecnología Emergente, Georgetown) +* Charlie Pownall a través del repositorio de controversias e incidentes algorítmicos y de automatización [(AIAAIC)](https://www.aiaaic.org/) * Lawrence Lee, Darlena Phuong Quyen Nguyen, Iftekhar Ahmed (UC Irvine) -Hay una comunidad creciente de personas preocupadas por la recopilación y caracterización de incidentes de IA, y alentamos a todos a contribuir al desarrollo de este sistema. +Existe una comunidad cada vez mayor de personas preocupadas por la recopilación y caracterización de incidentes de IA, y animamos a todos a contribuir al desarrollo de este sistema.
diff --git a/site/gatsby-site/content/about/index.fr.mdx b/site/gatsby-site/content/about/index.fr.mdx index ce57334ee7..3dac6abdb7 100644 --- a/site/gatsby-site/content/about/index.fr.mdx +++ b/site/gatsby-site/content/about/index.fr.mdx @@ -9,90 +9,112 @@ aiTranslated: true import Leaderboards from '../../src/components/landing/Leaderboards'; import Sponsors from '../../src/components/landing/Sponsors'; -# Pourquoi "Incidents IA" ? +## Pourquoi "Incidents IA"? -Les systèmes intelligents sont actuellement sujets à des pannes imprévues et souvent dangereuses lorsqu'ils sont déployés dans le monde réel. Tout comme le secteur des transports avant lui (par exemple, [FAA](https://www.faa.gov/data_research/accident_incident/) et [FARS](https://www.nhtsa.gov/research-data/fatality-analysis-reporting-system-fars)) et plus récemment [systèmes informatiques](https://cve.mitre.org/cve/), les systèmes intelligents nécessitent un référentiel des problèmes rencontrés dans le monde réel afin que les futurs chercheurs et développeurs puissent atténuer ou éviter les mauvais résultats répétés. +Les systèmes intelligents sont actuellement sujets à des pannes imprévues et souvent dangereuses lorsqu'ils sont déployés dans le monde réel. Tout comme le secteur des transports avant lui (par exemple, [FAA](https://www.faa.gov/data_research/accident_incident/) et [FARS](https://www.nhtsa.gov/research-data/fatality-Analysis-reporting-system-fars)) et plus récemment [systèmes informatiques](https://cve.mitre.org/cve/), les systèmes intelligents nécessitent un référentiel de problèmes rencontrés dans le monde réel afin que les futurs chercheurs et développeurs puissent atténuer ou éviter les mauvais résultats répétés. -# Qu'est-ce qu'un incident ? +## Qu'est-ce qu'un incident ? -L'ensemble initial de plus de 1 000 rapports d'incidents a été intentionnellement large. Les exemples actuels incluent, +La série initiale de plus de 1 000 rapports d’incidents était intentionnellement large. Les exemples actuels incluent, * Une *voiture autonome* [tue un piéton](/cite/4) -* Un *algorithme de trading* provoque un marché "[flash crash](/cite/28)" où des milliards de dollars sont transférés entre les parties +* Un *algorithme de trading* provoque un "[flash crash](/cite/28)" de marché où des milliards de dollars sont transférés entre les parties. * Un *système de reconnaissance faciale* [provoque l'arrestation d'une personne innocente](/cite/74) -Vous êtes invité à [explorer les incidents collectés à ce jour](/apps/discover), [afficher la liste complète](/summaries/incidents) et [soumettre](/apps/submit) des rapports d'incident supplémentaires. Les chercheurs sont invités à consulter notre [définition de travail des incidents d'IA](/research/1-criteria). -# Utilisateurs actuels et futurs +Vous êtes invité à [explorer les incidents collectés à ce jour](/apps/discover), [afficher la liste complète](/summaries/incidents) et à [soumettre](/apps/submit) des rapports d'incident supplémentaires. Les chercheurs sont invités à consulter notre [définition pratique des incidents d’IA](/research/1-criteria). -La base de données est [en constante évolution](/research/2-roadmap) [produit de données](/develop) et [collection d'applications](/apps). +## Utilisateurs actuels et futurs -* **Les utilisateurs actuels** incluent les architectes système, les développeurs de produits industriels, les responsables des relations publiques, les [chercheurs](/recherche) et les chercheurs en politique publique. Ces utilisateurs sont invités à utiliser l'application [Discover](/apps/discover) pour découvrir de manière proactive comment les systèmes intelligents récemment déployés ont produit des résultats inattendus dans le monde réel. Ce faisant, ils peuvent éviter de commettre des erreurs similaires dans leur développement. -* **Les utilisations futures** vont [évoluer](/research/2-roadmap) grâce aux contributions de code de la communauté [open source](https://github.com/PartnershipOnAI/aiid), y compris une base de données supplémentaire [summaries](/summaries) et [taxonomies](/research/2-roadmap). +La base de données est un produit de données et un ensemble d'applications en constante évolution. -# Quand devez-vous signaler un incident ? +* **Les utilisateurs actuels** incluent les architectes système, les développeurs de produits industriels, les responsables des relations publiques, les [chercheurs](/research) et les chercheurs en politiques publiques. Ces utilisateurs sont invités à utiliser l'application [Discover](/apps/discover) pour découvrir de manière proactive comment les systèmes intelligents récemment déployés ont produit des résultats inattendus dans le monde réel. Ce faisant, ils pourraient éviter de commettre des erreurs similaires dans leur développement. +* **Les utilisations futures** [évolueront](/research/2-roadmap) grâce aux contributions de code de la communauté [open source](https://github.com/responsible-ai-collaborative/aiid), y compris des base de données [résumés](/about_apps/) et [taxonomies](/research/2-roadmap). -En cas de doute sur la qualification d'un événement en tant qu'incident, veuillez le [soumettre](/apps/submit) ! Ce projet vise à converger vers une [définition partagée](/research/1-criteria) d'« incident d'IA » grâce à l'exploration des incidents candidats soumis par la communauté au sens large. -#Conseil d'administration +## Quand devez-vous signaler un incident ? -La base de données des incidents est gérée de manière participative par des personnes et des organisations qui contribuent au code, à la recherche et aux impacts plus larges. Si vous souhaitez participer à la gouvernance du projet, veuillez nous [contact](/contact) et inclure votre contribution prévue à la base de données des incidents d'IA. +En cas de doute quant à savoir si un événement est considéré comme un incident, veuillez le [soumettre](/apps/submit) ! Ce projet vise à converger vers une [définition partagée](/research/1-criteria) d'« incident d'IA » à travers l'exploration des incidents candidats soumis par la communauté au sens large. + +## Conseil d'administration + +La base de données des incidents est gérée de manière participative par des personnes et des organisations qui contribuent au code, à la recherche et à des impacts plus larges. Si vous souhaitez participer à la gouvernance du projet, veuillez nous [contacter](/contact) et inclure votre contribution prévue à la base de données des incidents AI. **Membres votants** -* **[Helen Toner](https://cset.georgetown.edu/staff/helen-toner/):** Helen Toner est directrice de la stratégie au Centre pour la sécurité et les technologies émergentes (CSET) de Georgetown. Elle a précédemment travaillé en tant qu'analyste de recherche senior chez Open Philanthropy, où elle a conseillé les décideurs politiques et les bailleurs de fonds sur la politique et la stratégie d'IA. Entre son travail chez Open Philanthropy et son arrivée au CSET, Helen a vécu à Pékin, étudiant l'écosystème chinois de l'IA en tant que chercheur affilié au Centre pour la gouvernance de l'IA de l'Université d'Oxford. Helen a écrit pour les Affaires étrangères et d'autres médias sur les implications de l'IA et de l'apprentissage automatique en matière de sécurité nationale pour la Chine et les États-Unis, et a témoigné devant la Commission d'examen de l'économie et de la sécurité entre les États-Unis et la Chine. Elle est membre du conseil d'administration d'OpenAI. Helen est titulaire d'une maîtrise en études de sécurité de Georgetown, ainsi que d'une licence en génie chimique et d'un diplôme en langues de l'Université de Melbourne. -**Contributions :** [Recherche sur les incidents liés à l'IA](https://cset.georgetown.edu/publication/ai-accidents-an-emerging-threat/) et supervision de la [taxonomie CSET](https://incidentdatabase.ai/taxonomie/cset). +* **[Patrick Hall](https://business.gwu.edu/patrick-hall):** Patrick était récemment scientifique principal chez bnh.ai, un cabinet d'avocats basé à Washington et spécialisé dans l'IA et l'analyse de données. Patrick est également professeur adjoint à la George Washington University School of Business. Avant de co-fonder bnh.ai, Patrick a dirigé les efforts responsables d’IA au sein de la société de logiciels d’apprentissage automatique H2O.ai, où ses travaux ont abouti à l’une des premières solutions commerciales au monde pour un apprentissage automatique explicable et équitable. Entre autres écrits dans les médias universitaires et technologiques, Patrick est le principal auteur de livres électroniques populaires sur l'apprentissage automatique explicable et responsable. Patrick a étudié la chimie computationnelle à l'Université de l'Illinois avant d'être diplômé de l'Institute for Advanced Analytics de la North Carolina State University. +**Contributions :** Patrick est l'un des principaux contributeurs de rapports d'incidents à la base de données d'incidents AI (https://incidentdatabase.ai/summaries/leaderboard) et assure un leadership stratégique pour le conseil d'administration. -* **[Patrick Hall](https://business.gwu.edu/johnston-patrick-hall):** Patrick est scientifique principal chez bnh.ai, un cabinet d'avocats basé à D.C. spécialisé dans l'IA et l'analyse de données. Patrick est également professeur invité à la George Washington University School of Business. Avant de cofonder bnh.ai, Patrick a dirigé les efforts d'IA responsable au sein de la société de logiciels d'apprentissage automatique H2O.ai, où son travail a abouti à l'une des premières solutions commerciales au monde pour un apprentissage automatique explicable et équitable. Parmi d'autres écrits académiques et technologiques, Patrick est le principal auteur de livres électroniques populaires sur l'apprentissage automatique explicable et responsable. Patrick a étudié la chimie computationnelle à l'Université de l'Illinois avant d'être diplômé de l'Institute for Advanced Analytics de la North Carolina State University. -**Contributions :** Patrick est le [leader](https://incidentdatabase.ai/summaries/leaderboard) contributeur des rapports d'incidents au projet de base de données des incidents d'IA. +* **[Heather Frase](https://cset.georgetown.edu/staff/heather-frase/):** Heather Frase, PhD, est chercheuse principale au [Centre pour la sécurité et les technologies émergentes] de Georgetown(https://cset.georgetown.edu/) (CSET), où elle travaille sur l'évaluation de l'IA. Elle est également conseillère non rémunérée pour le projet Open Loop de Meta, fournissant son expertise sur la mise en œuvre du cadre de gestion des risques liés à l’IA du National Institute for Standards and Technology. Avant de rejoindre le CSET, Heather a passé huit ans à fournir un support en matière d'analyse de données, de modélisation informatique, d'apprentissage automatique (ML) et d'intelligence artificielle (IA) pour les contrats de renseignement, de défense et fédéraux. De plus, Heather a passé 14 ans à l'Institut d'analyses de la défense (IDA), en tant que directrice adjointe des tests opérationnels et de l'évaluation (DOT&E). À l'IDA, elle a dirigé des équipes de recherche analytique qui ont appliqué leur expertise scientifique, technologique et statistique pour développer des mesures de données et des plans de collecte pour les tests opérationnels des principaux systèmes de défense, analyser les données de test et produire des évaluations de l'efficacité et de l'adéquation opérationnelles. Elle est titulaire d'un doctorat. en science des matériaux du California Institute of Technology et un BS en physique de l'Université de Miami à Oxford Ohio. +**Contributions:** Heather développe la recherche sur les incidents d'IA en plus de sa supervision de la [taxonomie CSET](https://incidentdatabase.ai/taxonomy/cset). -* **[Sean McGregor](https://seanbmcgregor.com/):** Sean McGregor a fondé le projet AI Incident Database et a récemment quitté un poste d'architecte d'apprentissage automatique au démarrage de l'accélérateur neuronal Syntiant afin qu'il puisse se concentrer sur l'assurance de systèmes intelligents à plein temps. Les travaux du Dr McGregor couvrent les accélérateurs neuronaux pour l'inférence écoénergétique, l'apprentissage en profondeur pour la parole et l'héliophysique, et l'apprentissage par renforcement pour la politique de suppression des incendies de forêt. En dehors de son travail rémunéré, Sean a organisé une série d'ateliers lors de grandes conférences universitaires sur l'IA sur le thème de "l'IA pour le bien" et développe actuellement une approche basée sur des incitations pour rendre l'IA plus sûre par le biais d'audits et d'assurances. **Contributions :** Soyez bénévoles en tant que responsable du projet et éditeur du projet AI Incident Database (AIID). +* **[Kristian J. Hammond](https://www.mccormick.northwestern.edu/research-faculty/directory/profiles/hammond-kristian.html):** Kris Hammond est professeur d'informatique Bill et Cathy Osborn Science à la Northwestern University et co-fondateur de la société d'intelligence artificielle Narrative Science, récemment acquise par Salesforce. Il est également directeur pédagogique de l’initiative CS + X de Northwestern, qui explore la manière dont la pensée informatique peut être utilisée pour transformer des domaines tels que le droit, la médecine, l’éducation et les affaires. Il est directeur du Master of Science in Artificial Intelligence (MSAI) de Northwestern. Plus récemment, le Dr Hammond a fondé le Center for Advancing Safety in Machine Intelligence (CASMI), un centre de recherche financé par Underwriter’s Laboratories. CASMI se concentre sur l’opérationnalisation de la conception et de l’évaluation des systèmes d’IA du point de vue de leur impact sur la vie et la sécurité humaines. +**Contributions :** Kris développe un projet collaboratif centré sur les études de cas d'incidents. -**Membres sans droit de vote** +**Conseil émérite** -* **[Neama Dadkhahnikoo](https://www.linkedin.com/in/neama/):** Neama Dadkhahnikoo est un expert en intelligence artificielle et en entrepreneuriat, avec plus de 15 ans d'expérience dans le développement technologique au sein de startups, non bénéfices et les grandes entreprises. Il est actuellement chef de produit pour Vertex AI, la plate-forme unifiée de Google Cloud pour l'apprentissage automatique de bout en bout. Auparavant, M. Dadkhahnikoo était directeur de l'IA et des opérations de données à la Fondation XPRIZE, CTO de CaregiversDirect (start-up d'IA pour les soins à domicile), co-fondateur et COO de Textpert (start-up d'IA pour la santé mentale) et consultant en startup. Il a commencé sa carrière en tant que développeur de logiciels pour The Boeing Company. M. Dadkhahnikoo est titulaire d'un MBA de UCLA Anderson ; une maîtrise en gestion de projet du Stevens Institute of Technology; et un BA en mathématiques appliquées et en informatique, avec une mineure en physique, de l'UC Berkeley. -**Contributions :** Neama se porte volontaire pour maintenir l'organisation de la Responsible AI Collaborative en dehors de son rôle de chef de produit chez Google. -* **[Kit Harris](https://www.linkedin.com/in/kitharris/):** Le kit mène des enquêtes sur les subventions et recherche des domaines prometteurs chez Longview Philanthropy. Il écrit également sur les recommandations et les recherches de Longview pour les philanthropes clients de Longview. Avant de se concentrer sur un travail philanthropique à fort impact, Kit a travaillé comme trader de dérivés de crédit avec J.P. Morgan. Pendant ce temps, il a fait don de la majorité de ses revenus à des œuvres caritatives à fort impact. Kit est titulaire d'un diplôme de premier cycle en mathématiques de l'Université d'Oxford. -**Contributions :** Kit sert d'observateur au conseil d'administration, fournit des conseils stratégiques et est le point de contact de l'IRAC à Longview. +Les membres émérites du conseil d’administration sont ceux qui se sont particulièrement distingués dans leur service au sein du Responsible AI Collaborative. Ils n’occupent aucun poste de gouvernance au sein de l’organisation. -# Collaborateurs +* **[Sean McGregor](https://seanbmcgregor.com/) :** Sean McGregor a fondé le projet AI Incident Database et a récemment rejoint le [Digital Safety Research Institute](https://ul.org/research/digital-safety) en tant que directeur fondateur. Avant de démarrer Responsible AI Collaborative, Sean a quitté son poste d'architecte d'apprentissage automatique au sein de la start-up d'accélérateurs neuronaux Syntiant afin de pouvoir se concentrer à plein temps sur l'assurance de systèmes intelligents. Les travaux du Dr McGregor couvrent les accélérateurs neuronaux pour l'inférence économe en énergie, l'apprentissage profond pour la parole et l'héliophysique, et l'apprentissage par renforcement pour les politiques de suppression des incendies de forêt. En dehors de son travail rémunéré, Sean a organisé une série d'ateliers lors de grandes conférences universitaires sur l'IA sur le thème « l'IA pour le bien » et cherche désormais à tirer parti en toute sécurité des avantages de l'IA en reliant les enregistrements d'incidents d'IA aux programmes de test d'IA. -**Contributeurs Open Source :** Personnes ayant contribué à plusieurs requêtes d'extraction, graphiques, copies de site ou rapports de bogues à la base de données des incidents d'IA. +* **[Helen Toner](https://cset.georgetown.edu/staff/helen-toner/) :** Helen Toner est directrice de la stratégie au Centre pour la sécurité et les technologies émergentes (CSET) de Georgetown. Elle a auparavant travaillé comme analyste de recherche principale chez Open Philanthropy, où elle a conseillé les décideurs politiques et les bailleurs de fonds sur la politique et la stratégie en matière d'IA. Entre son travail chez Open Philanthropy et son arrivée au CSET, Helen a vécu à Pékin, étudiant l'écosystème chinois de l'IA en tant que chercheur affilié au Centre pour la gouvernance de l'IA de l'Université d'Oxford. Helen a écrit pour Foreign Affairs et d'autres médias sur les implications de l'IA et de l'apprentissage automatique sur la sécurité nationale pour la Chine et les États-Unis, et a témoigné devant la Commission d'examen économique et de sécurité entre les États-Unis et la Chine. Elle est membre du conseil d'administration d'OpenAI. Helen est titulaire d'une maîtrise en études de sécurité de Georgetown, ainsi que d'un baccalauréat en génie chimique et d'un diplôme en langues de l'Université de Melbourne. -* [César Varela](https://github.com/cesarvarela) -* [Alex Musca](https://github.com/alexmcode) -* [Chloé Kam](http://kamchy.com) (Logos AIID) -* [JT McHorse](https://github.com/jt-mchorse) -* Seth Reid +## Collaborateurs -**Partenariat avec les membres du personnel d'AI :** -[Jingying Yang](https://www.linkedin.com/in/jingyingyang/) et [Dr. Christine Custis](https://www.su.edu/symposium/business-symposium-speakers/christine-custis/) a contribué de manière significative aux premières étapes de l'AIID. +**Responsible AI Collaborative :** Personnes qui servent l'organisation derrière la base de données des incidents AI. + +* [Scott Allen Cambo](https://www.scottallencambo.com/) +* [Janet Boutilier Schwartz](https://www.linkedin.com/in/janet-boutilier-schwartz/) + +**Digital Safety Research Institute (DSRI) :** Personnes affiliées au [DSRI](https://ul.org/research/digital-safety), qui apporte un soutien substantiel au programme AIID. + +* [Kevin Paeth](https://www.linkedin.com/in/kevinpaeth/) est un responsable du DSRI +* [César Varela](https://github.com/cesarvarela) est un ingénieur Full Stack +* [Luna McNulty](https://lmcnulty.me/) est ingénieur UX +* [Pablo Costa](https://www.linkedin.com/in/pablo-costa/) est un ingénieur Full Stack +* [Clara Youdale Pinelli](https://www.linkedin.com/in/clarayoudale/) est une ingénieure front-end +* [Sean McGregor](https://seanbmcgregor.com/) est directeur chez DSRI -**Éditeurs d'incidents :** personnes qui résolvent les incidents soumis à la base de données. +**Éditeurs d'incidents :** personnes qui résolvent les incidents soumis à la base de données et les maintiennent. +* [Daniel Atherton](https://www.linkedin.com/in/daniel-atherton-167819251/) * [Sean McGregor](https://seanbmcgregor.com/) * [Khoa Lam](https://www.linkedin.com/in/khoalklam/) * [Kate Perkins](https://www.linkedin.com/in/kateeperkins/) * [Janet Boutilier Schwartz](https://www.linkedin.com/in/janet-boutilier-schwartz/) -* [Daniel Atherton](https://www.linkedin.com/in/daniel-atherton-167819251/) -**Editeurs de taxonomie :** Organisations ou personnes qui ont contribué [taxonomies](https://incidentdatabase.ai/taxonomies) à la base de données. +De plus, [Zachary Arnold](https://cset.georgetown.edu/staff/zachary-arnold/) a apporté des contributions significatives aux [critères d'incident](/research/1-criteria). + +**Éditeurs de taxonomie :** Organisations ou personnes qui ont contribué aux [taxonomies](https://incidentdatabase.ai/taxonomies) à la base de données. + +* [Centre pour la sécurité et les technologies émergentes](https://cset.georgetown.edu/) (CSET) +* [Nikiforos Pittaras](https://npit.github.io/) (GMF) -* [Centre pour la sécurité et les technologies émergentes] (https://cset.georgetown.edu/) (CSET) +**Partenariat sur les membres du personnel d'IA :** +[Jingying Yang](https://www.linkedin.com/in/jingyingyang/) et [Dr. Christine Custis](https://www.su.edu/symposium/business-symposium-speakers/christine-custis/) a contribué de manière significative aux premières étapes de l'AIID. + +**Contributeurs Open Source :** Personnes qui ont contribué à plus d'une demande d'extraction, de graphiques, de copie de site ou de rapport de bogue à la base de données d'incidents AI. + +* [Neama Dadkhahnikoo](https://www.linkedin.com/in/neama/) : Neama a été directrice exécutive bénévole et observatrice du conseil d'administration du Responsible AI Collaborative. +* [Kit Harris](https://www.linkedin.com/in/kitharris/) : Kit a servi en tant qu'observateur au conseil d'administration et a fourni des conseils stratégiques depuis son poste de conseiller en subventions. +* [Alex Muscă](https://github.com/alexmcode) +* [Chloe Kam](http://kamchy.com) Développement du logo AIID +* [JT McHorse](https://github.com/jt-mchorse) +*Seth Reid -**Contributeurs d'incidents :** personnes qui ont contribué à un grand nombre d'incidents dans la base de données. +**Contributeurs à l'incident :** Personnes qui ont contribué à un grand nombre d'incidents dans la base de données. * [Roman Lutz (Institut Max Planck pour les systèmes intelligents, anciennement Microsoft)](/apps/discover?display=details&lang=en&page=1&submitters=Roman%20Lutz) * [Patrick Hall (Burt and Hall LLP)](/apps/discover?display=details&lang=en&page=1&submitters=Patrick%20Hall%20%28BNH.ai%29) -* [Catherine Olsson (Google)](/apps/discover?display=details&lang=fr&page=1&submitters=Catherine%20Olsson) +* [Catherine Olsson (Google)](/apps/discover?display=details&lang=en&page=1&submitters=Catherine%20Olsson) * [Roman Yampolskiy (Université de Louisville)](/apps/discover?display=details&lang=en&page=1&submitters=Roman%20Yampolskiy) -* Sam Yoon (en tant qu'entrepreneur de PAI, puis de Deloitte Consulting, puis de la Kennedy School of Government) +* Sam Yoon (en tant qu'entrepreneur chez PAI, puis chez Deloitte Consulting, puis avec la Kennedy School of Government) -Les personnes suivantes ont collecté un grand nombre d'incidents en attente d'ingestion. +Les personnes suivantes ont collecté un grand nombre d’incidents en attente d’ingestion. * Zachary Arnold, Helen Toner, Ingrid Dickinson, Thomas Giallella et Nicolina Demakos (Centre pour la sécurité et les technologies émergentes, Georgetown) -* Charlie Pownall via AI, référentiel d'incidents et de controverses algorithmiques et d'automatisation [(AIAAIC)](https://www.aiaaic.org/) -*Lawrence Lee, Darlena Phuong Quyen Nguyen, Iftekhar Ahmed (UC Irvine) +* Charlie Pownall via le référentiel d'incidents et de controverses d'IA, d'algorithme et d'automatisation [(AIAAIC)](https://www.aiaaic.org/) +* Lawrence Lee, Darlena Phuong Quyen Nguyen, Iftekhar Ahmed (UC Irvine) -Il existe une communauté croissante de personnes concernées par la collecte et la caractérisation des incidents d'IA, et nous encourageons tout le monde à contribuer au développement de ce système. +Il existe une communauté croissante de personnes concernées par la collecte et la caractérisation des incidents d’IA, et nous encourageons chacun à contribuer au développement de ce système.
diff --git a/site/gatsby-site/content/about/index.mdx b/site/gatsby-site/content/about/index.mdx index b795b6e1fe..551f16699d 100644 --- a/site/gatsby-site/content/about/index.mdx +++ b/site/gatsby-site/content/about/index.mdx @@ -24,10 +24,10 @@ You are invited to [explore the incidents collected to date](/apps/discover), [v ## Current and Future Users -The database is a [constantly evolving](/research/2-roadmap) [data product](/develop) and [collection of applications](/apps). +The database is a constantly evolving data product and collection of applications. * **Current Users** include system architects, industrial product developers, public relations managers, [researchers](/research), and public policy researchers. These users are invited to use the [Discover](/apps/discover) application to proactively discover how recently deployed intelligent systems have produced unexpected outcomes in the real world. In so doing, they may avoid making similar mistakes in their development. -* **Future Uses** will [evolve](/research/2-roadmap) through the code contributions of the [open source](https://github.com/PartnershipOnAI/aiid) community, including additional database [summaries](/summaries) and [taxonomies](/research/2-roadmap). +* **Future Uses** will [evolve](/research/2-roadmap) through the code contributions of the [open source](https://github.com/responsible-ai-collaborative/aiid) community, including additional database [summaries](/about_apps/) and [taxonomies](/research/2-roadmap). ## When Should You Report an Incident? @@ -39,55 +39,66 @@ The incident database is managed in a participatory manner by persons and organi **Voting Members** -* **[Helen Toner](https://cset.georgetown.edu/staff/helen-toner/):** Helen Toner is Director of Strategy at Georgetown’s Center for Security and Emerging Technology (CSET). She previously worked as a Senior Research Analyst at Open Philanthropy, where she advised policymakers and grantmakers on AI policy and strategy. Between working at Open Philanthropy and joining CSET, Helen lived in Beijing, studying the Chinese AI ecosystem as a Research Affiliate of Oxford University’s Center for the Governance of AI. Helen has written for Foreign Affairs and other outlets on the national security implications of AI and machine learning for China and the United States, as well as testifying before the U.S.-China Economic and Security Review Commission. She is a member of the board of directors for OpenAI. Helen holds an MA in Security Studies from Georgetown, as well as a BSc in Chemical Engineering and a Diploma in Languages from the University of Melbourne. -**Contributions:** [AI incident research](https://cset.georgetown.edu/publication/ai-accidents-an-emerging-threat/) and oversight of the [CSET taxonomy](https://incidentdatabase.ai/taxonomy/cset). +* **[Patrick Hall](https://business.gwu.edu/johnston-patrick-hall):** Patrick was recently principal scientist at bnh.ai, a D.C.-based law firm specializing in AI and data analytics. Patrick is also assistant professor at the George Washington University School of Business. Before co-founding bnh.ai, Patrick led responsible AI efforts at the machine learning software firm H2O.ai, where his work resulted in one of the world’s first commercial solutions for explainable and fair machine learning. Among other academic and technology media writing, Patrick is the primary author of popular e-books on explainable and responsible machine learning. Patrick studied computational chemistry at the University of Illinois before graduating from the Institute for Advanced Analytics at North Carolina State University. +**Contributions:** Patrick is a [leading](https://incidentdatabase.ai/summaries/leaderboard) contributor of incident reports to the AI Incident Database and provides strategic leadership for the board. -* **[Patrick Hall](https://business.gwu.edu/johnston-patrick-hall):** Patrick is principal scientist at bnh.ai, a D.C.-based law firm specializing in AI and data analytics. Patrick also serves as visiting faculty at the George Washington University School of Business. Before co-founding bnh.ai, Patrick led responsible AI efforts at the machine learning software firm H2O.ai, where his work resulted in one of the world’s first commercial solutions for explainable and fair machine learning. Among other academic and technology media writing, Patrick is the primary author of popular e-books on explainable and responsible machine learning. Patrick studied computational chemistry at the University of Illinois before graduating from the Institute for Advanced Analytics at North Carolina State University. -**Contributions:** Patrick is the [leading](https://incidentdatabase.ai/summaries/leaderboard) contributor of incident reports to the AI Incident Database Project. +* **[Heather Frase](https://cset.georgetown.edu/staff/heather-frase/):** Heather Frase, PhD is a Senior Fellow at Georgetown’s [Center for Security and Emerging Technology](https://cset.georgetown.edu/) (CSET), where she works on AI Assessment. She also serves as an unpaid advisor to Meta’s Open Loop project, providing expertise on implementation of the National Institute for Standards and Technology’s AI Risk Management Framework. Prior to joining CSET, Heather spent eight years providing data analytics, computational modeling, Machine Learning (ML), and Artificial Intelligence (AI) support for Intelligence, Defense, and Federal contracts. Additionally, Heather spent 14 years at the Institute for Defense Analyses (IDA), supporting Director, Operational Test and Evaluation (DOT&E). At IDA she led analytic research teams to apply scientific, technological, and statistical expertise to develop data metrics and collection plans for operational tests of major defense systems, analyze test data, and produce assessments of operational effectiveness and suitability. She has a Ph.D. in Material Science from the California Institute of Technology and a BS in Physics from Miami University in Oxford Ohio. +**Contributions:** Heather develops AI incident research in addition to her oversight of the [CSET taxonomy](https://incidentdatabase.ai/taxonomy/cset). -* **[Sean McGregor](https://seanbmcgregor.com/):** Sean McGregor founded the AI Incident Database project and recently left a position as machine learning architect at the neural accelerator startup Syntiant so he could focus on the assurance of intelligent systems full time. Dr. McGregor's work spans neural accelerators for energy efficient inference, deep learning for speech and heliophysics, and reinforcement learning for wildfire suppression policy. Outside his paid work, Sean organized a series of workshops at major academic AI conferences on the topic of "AI for Good" and is currently developing an incentives-based approach to making AI safer through audits and insurance. **Contributions:** Sean volunteers as a project maintainer and editor of the AI Incident Database (AIID) project. +* **[Kristian J. Hammond](https://www.mccormick.northwestern.edu/research-faculty/directory/profiles/hammond-kristian.html):** Kris Hammond is the Bill and Cathy Osborn Professor of Computer Science at Northwestern University and the co-founder of the Artificial Intelligence company Narrative Science, recently acquired by Salesforce. He is also the faculty lead of Northwestern’s CS + X initiative, exploring how computational thinking can be used to transform fields such as the law, medicine, education, and business. He is director of Northwestern’s Master of Science in Artificial Intelligence (MSAI). Most recently, Dr. Hammond founded the Center for Advancing Safety in Machine Intelligence (CASMI), a research hub funded by Underwriter’s Laboratories. CASMI is focused on operationalizing the design and evaluation of AI systems from the perspective of their impact on human life and safety. +**Contributions:** Kris is developing a collaborative project centered on the case studies of incidents. -**Non-Voting Members** +**Emeritus Board** -* **[Neama Dadkhahnikoo](https://www.linkedin.com/in/neama/):** Neama Dadkhahnikoo is an expert in artificial intelligence and entrepreneurship, with over 15 years of experience in technology development at startups, non profits, and large companies. He currently serves as a Product Manager for Vertex AI, Google Cloud’s unified platform for end-to-end machine learning. Previously, Mr. Dadkhahnikoo was the Director of AI and Data Operations at the XPRIZE Foundation, CTO of CaregiversDirect (AI startup for home care), co-founder and COO of Textpert (AI startup for mental health), and a startup consultant. He started his career as a Software Developer for The Boeing Company. Mr. Dadkhahnikoo holds an MBA from UCLA Anderson; an MS in Project Management from Stevens Institute of Technology; and a BA in Applied Mathematics and Computer Science, with a minor in Physics, from UC Berkeley. -**Contributions:** Neama serves as Executive Director to the Responsible AI Collaborative outside his role as product manager at Google. -* **[Kit Harris](https://www.linkedin.com/in/kitharris/):** Kit leads on grant investigations and researches promising fields at Longview Philanthropy. He also writes about Longview recommendations and research for Longview client philanthropists. Prior to focusing on high-impact philanthropic work, Kit worked as a credit derivatives trader with J.P. Morgan. During that time, he donated the majority of his income to high-impact charities. Kit holds a first class degree in mathematics from the University of Oxford. -**Contributions:** Kit serves as board observer, provides strategic advice, and is RAIC's point of contact at Longview. +Emeritus board members are those that have particularly distinguished themselves in their service to the Responsible AI Collaborative. They hold no governance position within the organization. -## Collaborators +* **[Sean McGregor](https://seanbmcgregor.com/):** Sean McGregor founded the AI Incident Database project and recently joined the [Digital Safety Research Institute](https://ul.org/research/digital-safety) as a founding director. Prior to starting the Responsible AI Collaborative, Sean left a position as machine learning architect at the neural accelerator startup Syntiant so he could focus on the assurance of intelligent systems full time. Dr. McGregor's work spans neural accelerators for energy efficient inference, deep learning for speech and heliophysics, and reinforcement learning for wildfire suppression policy. Outside his paid work, Sean organized a series of workshops at major academic AI conferences on the topic of "AI for Good" and now seeks to safely realize the benefits of AI by bridging AI incident records to AI test programs. -**Open Source Contributors:** People that have contributed more than one pull request, graphics, site copy, or bug report to the AI Incident Database. +* **[Helen Toner](https://cset.georgetown.edu/staff/helen-toner/):** Helen Toner is Director of Strategy at Georgetown’s Center for Security and Emerging Technology (CSET). She previously worked as a Senior Research Analyst at Open Philanthropy, where she advised policymakers and grantmakers on AI policy and strategy. Between working at Open Philanthropy and joining CSET, Helen lived in Beijing, studying the Chinese AI ecosystem as a Research Affiliate of Oxford University’s Center for the Governance of AI. Helen has written for Foreign Affairs and other outlets on the national security implications of AI and machine learning for China and the United States, as well as testifying before the U.S.-China Economic and Security Review Commission. She is a member of the board of directors for OpenAI. Helen holds an MA in Security Studies from Georgetown, as well as a BSc in Chemical Engineering and a Diploma in Languages from the University of Melbourne. -* [César Varela](https://github.com/cesarvarela) is a Full Stack Engineer with the Responsible AI Collaborative -* [Luna McNulty](https://lmcnulty.me/) is a UX Engineer with the Responsible AI Collaborative -* [Pablo Costa](https://www.linkedin.com/in/pablo-costa/) is a Full Stack Engineer with the Responsible AI Collaborative -* [Clara Youdale Pinelli](https://www.linkedin.com/in/clarayoudale/) is a Front End Engineer with the Responsible AI Collaborative -* [Alex Muscă](https://github.com/alexmcode) -* [Chloe Kam](http://kamchy.com) Developed the AIID logo -* [JT McHorse](https://github.com/jt-mchorse) -* Seth Reid +## Collaborators **Responsible AI Collaborative:** People that serve the organization behind the AI Incident Database. +* [Scott Allen Cambo](https://www.scottallencambo.com/) * [Janet Boutilier Schwartz](https://www.linkedin.com/in/janet-boutilier-schwartz/) +**Digital Safety Research Institute (DSRI):** People affiliated with [DSRI](https://ul.org/research/digital-safety), which provides substantial support to the AIID program. + +* [Kevin Paeth](https://www.linkedin.com/in/kevinpaeth/) is a lead with DSRI +* [César Varela](https://github.com/cesarvarela) is a Full Stack Engineer +* [Luna McNulty](https://lmcnulty.me/) is a UX Engineer +* [Pablo Costa](https://www.linkedin.com/in/pablo-costa/) is a Full Stack Engineer +* [Clara Youdale Pinelli](https://www.linkedin.com/in/clarayoudale/) is a Front End Engineer +* [Sean McGregor](https://seanbmcgregor.com/) is a director with DSRI + **Incident Editors:** People that resolve incident submissions to the database and maintain them. +* [Daniel Atherton](https://www.linkedin.com/in/daniel-atherton-167819251/) * [Sean McGregor](https://seanbmcgregor.com/) * [Khoa Lam](https://www.linkedin.com/in/khoalklam/) * [Kate Perkins](https://www.linkedin.com/in/kateeperkins/) * [Janet Boutilier Schwartz](https://www.linkedin.com/in/janet-boutilier-schwartz/) -* [Daniel Atherton](https://www.linkedin.com/in/daniel-atherton-167819251/) Additionally, [Zachary Arnold](https://cset.georgetown.edu/staff/zachary-arnold/) made significant contributions to the [incident criteria](/research/1-criteria). **Taxonomy Editors:** Organizations or people that have contributed [taxonomies](https://incidentdatabase.ai/taxonomies) to the database. * [Center for Security and Emerging Technology](https://cset.georgetown.edu/) (CSET) +* [Nikiforos Pittaras](https://npit.github.io/) (GMF) **Partnership on AI staff members:** [Jingying Yang](https://www.linkedin.com/in/jingyingyang/) and [Dr. Christine Custis](https://www.su.edu/symposium/business-symposium-speakers/christine-custis/) contributed significantly to the early stages of the AIID. +**Open Source Contributors:** People that have contributed more than one pull request, graphics, site copy, or bug report to the AI Incident Database. + +* [Neama Dadkhahnikoo](https://www.linkedin.com/in/neama/): Neama served as the volunteer executive director and board observer for the Responsible AI Collaborative +* [Kit Harris](https://www.linkedin.com/in/kitharris/): Kit served as board observer and provided strategic advice from his position as grant advisor. +* [Alex Muscă](https://github.com/alexmcode) +* [Chloe Kam](http://kamchy.com) Developed the AIID logo +* [JT McHorse](https://github.com/jt-mchorse) +* Seth Reid + **Incident Contributors:** People that have contributed a large numbers of incidents to the database. * [Roman Lutz (Max Planck Institute for Intelligent Systems, formerly Microsoft)](/apps/discover?display=details&lang=en&page=1&submitters=Roman%20Lutz) diff --git a/site/gatsby-site/cypress.config.js b/site/gatsby-site/cypress.config.js index 1dd28c4dad..34a1c1cf22 100644 --- a/site/gatsby-site/cypress.config.js +++ b/site/gatsby-site/cypress.config.js @@ -4,6 +4,7 @@ module.exports = defineConfig({ video: false, chromeWebSecurity: false, screenshotOnRunFailure: false, + defaultCommandTimeout: 8000, retries: { runMode: 2, openMode: 0, @@ -13,7 +14,13 @@ module.exports = defineConfig({ // We've imported your old cypress plugins here. // You may want to clean this up later by importing these. setupNodeEvents(on, config) { - return require('./cypress/plugins/index.js')(on, config); + if (process.env.INSTRUMENT) { + require('@cypress/code-coverage/task')(on, config); + } + + require('./cypress/plugins/index.js')(on, config); + + return config; }, baseUrl: 'http://localhost:8000/', }, diff --git a/site/gatsby-site/cypress/e2e/incidentVariants.cy.js b/site/gatsby-site/cypress/e2e/incidentVariants.cy.js index a55169e768..c02e029fba 100644 --- a/site/gatsby-site/cypress/e2e/incidentVariants.cy.js +++ b/site/gatsby-site/cypress/e2e/incidentVariants.cy.js @@ -6,7 +6,7 @@ import { isCompleteReport, VARIANT_STATUS, } from '../../src/utils/variants'; -import { format, getUnixTime } from 'date-fns'; +import { getUnixTime } from 'date-fns'; const { gql } = require('@apollo/client'); const incidentId = 464; @@ -192,14 +192,14 @@ describe('Variants pages', () => { (req) => req.body.operationName == 'UpdateVariant' && req.body.variables.query.report_number === variant.report_number && - req.body.variables.set.date_published === new_date_published && + req.body.variables.set.date_published === new Date(new_date_published).toISOString() && req.body.variables.set.submitters[0] === variant.submitters[0] && req.body.variables.set.submitters[1] === variant.submitters[1] && req.body.variables.set.text === new_text && req.body.variables.set.inputs_outputs[0] === new_inputs_outputs_1 && req.body.variables.set.inputs_outputs[1] === new_inputs_outputs_2 && req.body.variables.set.tags.includes(VARIANT_STATUS.approved) && - req.body.variables.set.date_modified == format(now, 'yyyy-MM-dd') && + req.body.variables.set.date_modified == now.toISOString() && req.body.variables.set.epoch_date_modified == getUnixTime(now), 'updateVariant', { @@ -269,14 +269,14 @@ describe('Variants pages', () => { (req) => req.body.operationName == 'UpdateVariant' && req.body.variables.query.report_number === variant.report_number && - req.body.variables.set.date_published === new_date_published && + req.body.variables.set.date_published === new Date(new_date_published).toISOString() && req.body.variables.set.submitters[0] === variant.submitters[0] && req.body.variables.set.submitters[1] === new_submitter && req.body.variables.set.text === new_text && req.body.variables.set.inputs_outputs[0] === new_inputs_outputs_1 && req.body.variables.set.inputs_outputs[1] === new_inputs_outputs_2 && req.body.variables.set.tags.includes(VARIANT_STATUS.rejected) && - req.body.variables.set.date_modified == format(now, 'yyyy-MM-dd') && + req.body.variables.set.date_modified == now.toISOString() && req.body.variables.set.epoch_date_modified == getUnixTime(now), 'updateVariant', { @@ -346,14 +346,14 @@ describe('Variants pages', () => { (req) => req.body.operationName == 'UpdateVariant' && req.body.variables.query.report_number === variant.report_number && - req.body.variables.set.date_published === new_date_published && + req.body.variables.set.date_published === new Date(new_date_published).toISOString() && req.body.variables.set.submitters[0] === variant.submitters[0] && req.body.variables.set.submitters[1] === new_submitter && req.body.variables.set.text === new_text && req.body.variables.set.inputs_outputs[0] === new_inputs_outputs_1 && req.body.variables.set.inputs_outputs[1] === variant.inputs_outputs[1] && req.body.variables.set.tags == undefined && - req.body.variables.set.date_modified == format(now, 'yyyy-MM-dd') && + req.body.variables.set.date_modified == now.toISOString() && req.body.variables.set.epoch_date_modified == getUnixTime(now), 'updateVariant', { diff --git a/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js index 18b68a8166..5e6bfdfdb3 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js @@ -127,4 +127,60 @@ describe('Checklists App Form', () => { }); }); }); + + maybeIt('Should trigger GraphQL update on removing tag', () => { + withLogin(({ user }) => { + interceptFindChecklist({ + ...defaultChecklist, + owner_id: user.userId, + tags_goals: ['GMF:Known AI Goal:Code Generation'], + }); + interceptUpsertChecklist({}); + + cy.visit(url); + + cy.get('[option="GMF:Known AI Goal:Code Generation"] .close').click(); + + cy.wait(['@upsertChecklist']).then((xhr) => { + expect(xhr.request.body.variables.checklist).to.deep.nested.include({ + tags_goals: [], + }); + }); + + cy.visit(url); + }); + }); + + it('Should remove a manually-created risk', () => { + withLogin(({ user }) => { + interceptFindChecklist({ + ...defaultChecklist, + owner_id: user.userId, + risks: [ + { + __typename: 'ChecklistRisk', + generated: false, + id: '5bb31fa6-2d32-4a01-b0a0-fa3fb4ec4b7d', + likelihood: '', + precedents: [], + risk_notes: '', + risk_status: 'Mitigated', + severity: '', + tags: ['GMF:Known AI Goal:Content Search'], + title: 'Manual Test Risk', + touched: false, + }, + ], + }); + interceptUpsertChecklist({ ...defaultChecklist, owner_id: user.userId }); + + cy.visit(url); + + cy.contains('Manual Test Risk').get('svg > title').contains('Delete Risk').parent().click(); + + cy.wait('@upsertChecklist'); + + cy.contains('Manual Test Risk').should('not.exist'); + }); + }); }); diff --git a/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js index a36b9fa779..760edaffd4 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js @@ -36,7 +36,13 @@ describe('Checklists App Index', () => { cy.get(newChecklistButtonQuery).should('exist'); }); - maybeIt('Should show delete buttons only for owned checklists', () => { + /* We're now showing only the user's owned checklists, + * so we can't test that the delete button doesn't show up on unowned ones. + * Eventually, we'll probably have public checklists show up here too, + * so this can be skipped with a TODO to activate it + * once there are unowned checklists displayed. + */ + it.skip('Should show delete buttons only for owned checklists', () => { cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); cy.query(usersQuery).then(({ data: { users } }) => { diff --git a/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js index 9f4eac0dcd..3c3c619002 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js @@ -171,21 +171,6 @@ describe('Submitted reports', () => { } ); - cy.conditionalIntercept( - '**/graphql', - (req) => - req.body.operationName == 'UpsertSubscription' && - req.body.variables?.query?.type === SUBSCRIPTION_TYPE.submissionPromoted, - 'UpsertSubscriptionPromoted', - { - data: { - upsertOneSubscription: { - _id: 'dummyIncidentId', - }, - }, - } - ); - cy.get('select[data-cy="promote-select"]').as('dropdown'); cy.get('@dropdown').select('Incident'); @@ -212,18 +197,6 @@ describe('Submitted reports', () => { expect(variables.subscription.userId.link).to.eq(user.userId); }); - cy.wait('@UpsertSubscriptionPromoted') - .its('request.body.variables') - .then((variables) => { - expect(variables.query.type).to.eq(SUBSCRIPTION_TYPE.submissionPromoted); - expect(variables.query.incident_id.incident_id).to.eq(182); - expect(variables.query.userId.userId).to.eq(submission.user.userId); - - expect(variables.subscription.type).to.eq(SUBSCRIPTION_TYPE.submissionPromoted); - expect(variables.subscription.incident_id.link).to.eq(182); - expect(variables.subscription.userId.link).to.eq(submission.user.userId); - }); - cy.contains( '[data-cy="toast"]', 'Successfully promoted submission to Incident 182 and Report 1565' @@ -795,6 +768,7 @@ describe('Submitted reports', () => { entities: [ { __typename: 'Entity', entity_id: 'Adults', name: 'adults' }, { __typename: 'Entity', entity_id: 'Google', name: 'google' }, + { __typename: 'Entity', entity_id: 'Tesla', name: 'tesla' }, ], }, } @@ -813,6 +787,13 @@ describe('Submitted reports', () => { 'value', 'YouTube to crack down on inappropriate content masked as kids’ cartoons' ); + + cy.get('input[name="harmed_parties"]').type('Tes'); + + cy.get('#harmed_parties-tags .dropdown-item') + .contains(/^Tesla$/) + .click(); + cy.get('input[label="Image Address"]').should( 'have.attr', 'value', @@ -897,80 +878,79 @@ describe('Submitted reports', () => { } ); - maybeIt( - 'Does not allow promotion of submission to Issue if schema is invalid (missing Title).', - () => { - cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); + it.only('Does not allow promotion of submission to Issue if schema is invalid (missing Title).', () => { + cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); - const submission = submittedReports.data.submissions.find( - (r) => r._id === '123461606b4bb5e39601234' - ); + const submission = submittedReports.data.submissions.find( + (r) => r._id === '123461606b4bb5e39601234' + ); - cy.conditionalIntercept( - '**/graphql', - (req) => req.body.operationName == 'FindSubmissions', - 'FindSubmissions', - { - data: { - submissions: [submission], - }, - } - ); + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'FindSubmissions', + 'FindSubmissions', + { + data: { + submissions: [submission], + }, + } + ); - cy.conditionalIntercept( - '**/graphql', - (req) => req.body.operationName == 'FindSubmission', - 'FindSubmission', - { - data: { - submission: submission, - }, - } - ); + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'FindSubmission', + 'FindSubmission', + { + data: { + submission: submission, + }, + } + ); - cy.conditionalIntercept( - '**/graphql', - (req) => req.body.operationName == 'AllQuickAdd', - 'AllQuickAdd', - { - data: { - quickadds: [quickAdds], - }, - } - ); + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'AllQuickAdd', + 'AllQuickAdd', + { + data: { + quickadds: [quickAdds], + }, + } + ); - cy.visit(url); + cy.visit(url); - cy.wait('@FindSubmissions'); + cy.waitForStableDOM(); - cy.visit(url + `?editSubmission=${submission._id}`); + cy.wait('@FindSubmissions'); - cy.wait('@AllQuickAdd'); + cy.visit(url + `?editSubmission=${submission._id}`); - cy.on('fail', (err) => { - expect(err.message).to.include( - '`cy.wait()` timed out waiting `2000ms` for the 1st request to the route: `promotionInvoked`. No request ever occurred.' - ); - }); + cy.wait('@AllQuickAdd'); - cy.conditionalIntercept( - '**/graphql', - (req) => req.body.operationName === 'PromoteSubmission', - 'promotionInvoked', - {} + cy.on('fail', (err) => { + expect(err.message).to.include( + '`cy.wait()` timed out waiting `2000ms` for the 1st request to the route: `promotionInvoked`. No request ever occurred.' ); + }); - cy.get('select[data-cy="promote-select"]').as('dropdown'); + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName === 'PromoteSubmission', + 'promotionInvoked', + {} + ); - cy.get('@dropdown').select('Issue'); + cy.get('select[data-cy="promote-select"]').as('dropdown'); - cy.get('[data-cy="promote-button"]').click(); + cy.get('@dropdown').select('Issue'); - cy.contains('[data-cy="toast"]', 'Title is required').should('exist'); + cy.get('[data-cy="promote-button"]').click(); - cy.wait('@promotionInvoked', { timeout: 2000 }); - } - ); + cy.contains('[data-cy="toast"]', 'Title is required').should('exist'); + + cy.wait('@promotionInvoked', { timeout: 2000 }); + }); maybeIt( 'Does not allow promotion of submission to Report if schema is invalid (missing Date).', diff --git a/site/gatsby-site/cypress/e2e/integration/apps/variants.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/variants.cy.js index 56d59263c6..7fa4ce23c3 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/variants.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/variants.cy.js @@ -499,14 +499,14 @@ describe('Variants App', () => { (req) => req.body.operationName == 'UpdateVariant' && req.body.variables.query.report_number === variant.report_number && - req.body.variables.set.date_published === new_date_published && + req.body.variables.set.date_published === new Date(new_date_published).toISOString() && req.body.variables.set.submitters[0] === variant.submitters[0] && req.body.variables.set.submitters[1] === new_submitter && req.body.variables.set.text === new_text && req.body.variables.set.inputs_outputs[0] === new_inputs_outputs_1 && req.body.variables.set.inputs_outputs[1] === undefined && req.body.variables.set.tags.includes(VARIANT_STATUS.approved) && - req.body.variables.set.date_modified == format(now, 'yyyy-MM-dd') && + req.body.variables.set.date_modified == now.toISOString() && req.body.variables.set.epoch_date_modified == getUnixTime(now), 'updateVariant', { diff --git a/site/gatsby-site/cypress/e2e/integration/cite.cy.js b/site/gatsby-site/cypress/e2e/integration/cite.cy.js index 4c723ae258..06072f4ebc 100644 --- a/site/gatsby-site/cypress/e2e/integration/cite.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/cite.cy.js @@ -396,7 +396,7 @@ describe('Cite pages', () => { cy.visit('/cite/9'); - cy.wait('@findIncident', { timeout: 10000 }); + cy.wait('@findIncident', { timeout: 30000 }); cy.waitForStableDOM(); @@ -688,4 +688,22 @@ describe('Cite pages', () => { cy.get('[data-cy="incident-form"]', { timeout: 8000 }).should('be.visible'); }); + + it('Should open incident from the discover app', { retries: { runMode: 4 } }, () => { + cy.visit(discoverUrl); + + cy.disableSmoothScroll(); + + cy.waitForStableDOM(); + + cy.get('[data-cy="collapse-button"]:visible').click(); + + cy.contains('Show Details on Incident #10').first().click(); + + cy.waitForStableDOM(); + + cy.url().should('include', '/cite/10'); + + cy.waitForStableDOM(); + }); }); diff --git a/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js b/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js index 7f42ddab19..3f79f81cea 100644 --- a/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js @@ -197,10 +197,9 @@ describe('Edit report', () => { const expectedReport = { authors: ['Test Author'], cloudinary_id: 'reports/test.com/test.jpg', - date_downloaded: '2022-01-01', + date_downloaded: new Date('2022-01-01').toISOString(), date_modified: format(now, 'yyyy-MM-dd'), - date_published: '2022-02-02', - epoch_date_downloaded: 1640995200, + date_published: new Date('2022-02-02').toISOString(), epoch_date_modified: getUnixTime(now), epoch_date_published: 1643760000, flag: null, @@ -220,8 +219,10 @@ describe('Edit report', () => { cy.wait('@updateReport').then((xhr) => { expect(xhr.request.body.variables.query.report_number).eq(expectedReport.report_number); - - expect(xhr.request.body.variables.set).to.deep.eq(expectedReport); + expect({ + ...xhr.request.body.variables.set, + date_modified: format(new Date(xhr.request.body.variables.set.date_modified), 'yyyy-MM-dd'), + }).to.deep.eq(expectedReport); }); cy.wait('@logReportHistory') @@ -232,6 +233,7 @@ describe('Edit report', () => { ...expectedReport, modifiedBy: user.userId, user: report10.data.report.user.userId, + date_modified: input.date_modified, }; expect(input).to.deep.eq(expectedResult); @@ -401,10 +403,9 @@ describe('Edit report', () => { const expectedReport = { authors: ['Test Author'], cloudinary_id: 'reports/test.com/test.jpg', - date_downloaded: '2022-01-01', + date_downloaded: new Date('2022-01-01').toISOString(), date_modified: format(now, 'yyyy-MM-dd'), - date_published: '2022-02-02', - epoch_date_downloaded: 1640995200, + date_published: new Date('2022-02-02').toISOString(), epoch_date_modified: getUnixTime(now), epoch_date_published: 1643760000, flag: null, @@ -424,8 +425,10 @@ describe('Edit report', () => { cy.wait('@updateReport').then((xhr) => { expect(xhr.request.body.variables.query.report_number).eq(10); - - expect(xhr.request.body.variables.set).deep.eq(expectedReport); + expect({ + ...xhr.request.body.variables.set, + date_modified: format(new Date(xhr.request.body.variables.set.date_modified), 'yyyy-MM-dd'), + }).to.deep.eq(expectedReport); }); cy.wait('@logReportHistory') @@ -436,6 +439,7 @@ describe('Edit report', () => { ...expectedReport, modifiedBy: user.userId, user: report10.data.report.user.userId, + date_modified: input.date_modified, }; expect(input).to.deep.eq(expectedResult); @@ -666,11 +670,10 @@ describe('Edit report', () => { authors: ['Marco Acevedo'], cloudinary_id: 'reports/assets.change.org/photos/0/yb/id/eYyBIdJOMHpqcty-1600x900-noPad.jpg?1523726975', - date_downloaded: '2019-04-13', + date_downloaded: new Date('2019-04-13').toISOString(), date_modified: format(now, 'yyyy-MM-dd'), - date_published: '2015-07-11', + date_published: new Date('2015-07-11').toISOString(), editor_notes: '', - epoch_date_downloaded: 1555113600, epoch_date_modified: getUnixTime(now), epoch_date_published: 1436572800, flag: null, @@ -692,7 +695,10 @@ describe('Edit report', () => { .its('request.body.variables') .then((variables) => { expect(variables.query.report_number).to.equal(23); - expect(variables.set).deep.eq(expectedReport); + expect({ + ...variables.set, + date_modified: format(new Date(variables.set.date_modified), 'yyyy-MM-dd'), + }).deep.eq(expectedReport); }); cy.wait('@logReportHistory') @@ -703,6 +709,7 @@ describe('Edit report', () => { ...expectedReport, modifiedBy: user.userId, user: report10.data.report.user.userId, + date_modified: input.date_modified, }; expect(input).to.deep.eq(expectedResult); @@ -934,8 +941,8 @@ describe('Edit report', () => { authors: ['Marco Acevedo'], cloudinary_id: 'reports/assets.change.org/photos/0/yb/id/eYyBIdJOMHpqcty-1600x900-noPad.jpg?1523726975', - date_downloaded: '2019-04-13', - date_published: '2015-07-11', + date_downloaded: new Date('2019-04-13').toISOString(), + date_published: new Date('2015-07-11').toISOString(), flag: null, image_url: 'https://assets.change.org/photos/0/yb/id/eYyBIdJOMHpqcty-1600x900-noPad.jpg?1523726975', @@ -950,7 +957,6 @@ describe('Edit report', () => { editor_notes: '', language: 'en', source_domain: 'change.org', - epoch_date_downloaded: 1555113600, epoch_date_published: 1436572800, date_modified: format(now, 'yyyy-MM-dd'), epoch_date_modified: getUnixTime(now), @@ -960,7 +966,10 @@ describe('Edit report', () => { .its('request.body.variables') .then((variables) => { expect(variables.query.report_number).to.equal(23); - expect(variables.set).deep.eq(expectedReport); + expect({ + ...variables.set, + date_modified: format(new Date(variables.set.date_modified), 'yyyy-MM-dd'), + }).deep.eq(expectedReport); }); cy.wait('@logReportHistory') @@ -971,6 +980,7 @@ describe('Edit report', () => { ...expectedReport, modifiedBy: user.userId, user: report10.data.report.user.userId, + date_modified: input.date_modified, }; expect(input).to.deep.eq(expectedResult); @@ -1128,8 +1138,8 @@ describe('Edit report', () => { authors: ['Marco Acevedo'], cloudinary_id: 'reports/assets.change.org/photos/0/yb/id/eYyBIdJOMHpqcty-1600x900-noPad.jpg?1523726975', - date_downloaded: '2019-04-13', - date_published: '2015-07-11', + date_downloaded: new Date('2019-04-13').toISOString(), + date_published: new Date('2015-07-11').toISOString(), flag: null, image_url: 'https://assets.change.org/photos/0/yb/id/eYyBIdJOMHpqcty-1600x900-noPad.jpg?1523726975', @@ -1144,7 +1154,6 @@ describe('Edit report', () => { editor_notes: '', language: 'en', source_domain: 'change.org', - epoch_date_downloaded: 1555113600, epoch_date_published: 1436572800, date_modified: format(now, 'yyyy-MM-dd'), epoch_date_modified: getUnixTime(now), @@ -1154,7 +1163,10 @@ describe('Edit report', () => { .its('request.body.variables') .then((variables) => { expect(variables.query.report_number).to.equal(23); - expect(variables.set).deep.eq(expectedReport); + expect({ + ...variables.set, + date_modified: format(new Date(variables.set.date_modified), 'yyyy-MM-dd'), + }).deep.eq(expectedReport); }); cy.wait('@logReportHistory') @@ -1165,6 +1177,7 @@ describe('Edit report', () => { ...expectedReport, modifiedBy: user.userId, user: report10.data.report.user.userId, + date_modified: input.date_modified, }; expect(input).to.deep.eq(expectedResult); diff --git a/site/gatsby-site/cypress/e2e/integration/discover.cy.js b/site/gatsby-site/cypress/e2e/integration/discover.cy.js index 5c6591a53d..db97d76102 100644 --- a/site/gatsby-site/cypress/e2e/integration/discover.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/discover.cy.js @@ -546,4 +546,32 @@ describe('The Discover app', () => { cy.get('div[data-cy="hits-container"]').children().should('have.length.at.least', 8); } ); + + conditionalIt( + !Cypress.env('isEmptyEnvironment'), + 'Search using the classifications filter', + () => { + cy.visit(url); + + cy.waitForStableDOM(); + + cy.get('[data-cy=expand-filters]').click(); + + cy.contains('button', 'Classifications').click(); + + cy.get('[data-cy="search"] input').type('Buenos Aires'); + + cy.get('[data-cy="attributes"] [data-cy="Named Entities"]').contains('Buenos Aires').click(); + + cy.waitForStableDOM(); + + cy.url().should('include', 'classifications=CSETv0%3ANamed%20Entities%3ABuenos%20Aires'); + + cy.get('[data-cy="selected-refinements"]') + .contains('CSETv0 : Named Entities : Buenos Aires') + .should('be.visible'); + + cy.get('div[data-cy="hits-container"]').children().should('have.length.at.least', 1); + } + ); }); diff --git a/site/gatsby-site/cypress/e2e/integration/incidents.cy.js b/site/gatsby-site/cypress/e2e/integration/incidents.cy.js index 564b32e302..ead9bec231 100644 --- a/site/gatsby-site/cypress/e2e/integration/incidents.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/incidents.cy.js @@ -27,8 +27,52 @@ describe('Incidents Summary', () => { cy.get('[data-cy="incident-list"] > div') .should('have.length', incidents.length) .and('be.visible'); + }); + } + ); + + conditionalIt( + !Cypress.env('isEmptyEnvironment'), + 'Should sort by Incident ID (ascending and descending)', + () => { + cy.visit(url); + + cy.query({ + query: gql` + { + incidents(limit: 9999, sortBy: INCIDENT_ID_DESC) { + incident_id + } + } + `, + }).then(({ data: { incidents } }) => { + cy.get('[data-cy="sort-ascending-button"]') + .should('exist') + .and('be.visible') + .and('not.be.disabled'); + cy.get('[data-cy="sort-descending-button"]') + .should('exist') + .and('be.visible') + .and('be.disabled'); - // could use some more toughly testing here + // Check default Incident ID descending order + cy.get('[data-cy="incident-list"] > div') + .first() + .should('contain', `Incident ${incidents[0].incident_id}`); + + // Check Incident ID ascending order + cy.get('[data-cy="sort-ascending-button"]').click(); + cy.waitForStableDOM(); + cy.get('[data-cy="incident-list"] > div') + .first() + .should('contain', `Incident ${incidents[incidents.length - 1].incident_id}`); + + // Check Incident ID descending order + cy.get('[data-cy="sort-descending-button"]').click(); + cy.waitForStableDOM(); + cy.get('[data-cy="incident-list"] > div') + .first() + .should('contain', `Incident ${incidents[0].incident_id}`); }); } ); diff --git a/site/gatsby-site/cypress/e2e/integration/incidents/history.cy.js b/site/gatsby-site/cypress/e2e/integration/incidents/history.cy.js index 7b78f935f4..2016d3be4e 100644 --- a/site/gatsby-site/cypress/e2e/integration/incidents/history.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/incidents/history.cy.js @@ -168,7 +168,7 @@ describe('Incidents', () => { cy.url().should('include', '/cite/10'); }); - conditionalIt( + conditionalIt.skip( !Cypress.env('isEmptyEnvironment'), 'Should refresh Report history if the user go back on the browser', () => { diff --git a/site/gatsby-site/cypress/e2e/integration/integrity.cy.js b/site/gatsby-site/cypress/e2e/integration/integrity.cy.js index b696d16596..528e778696 100644 --- a/site/gatsby-site/cypress/e2e/integration/integrity.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/integrity.cy.js @@ -16,7 +16,7 @@ const isLinked = (reportNumber, incidents) => { describe('Integrity', () => { it( `Shouldn't have repeated report numbers`, - { requestTimeout: 30000, defaultCommandTimeout: 30000, responseTimeout: 30000 }, + { requestTimeout: 60000, defaultCommandTimeout: 60000, responseTimeout: 60000 }, () => { cy.query({ query: gql` @@ -51,7 +51,7 @@ describe('Integrity', () => { it( `is_incident_report should be true for reports assigned to incidents and vice versa`, - { requestTimeout: 30000, defaultCommandTimeout: 30000, responseTimeout: 30000 }, + { requestTimeout: 60000, defaultCommandTimeout: 60000, responseTimeout: 60000 }, () => { cy.query({ query: gql` @@ -85,7 +85,7 @@ describe('Integrity', () => { it( `Classifications should be linked to one and only one incident`, - { requestTimeout: 30000, defaultCommandTimeout: 30000, responseTimeout: 30000 }, + { requestTimeout: 60000, defaultCommandTimeout: 60000, responseTimeout: 60000 }, () => { cy.query({ query: gql` diff --git a/site/gatsby-site/cypress/e2e/integration/pages.cy.js b/site/gatsby-site/cypress/e2e/integration/pages.cy.js index b180e7e78f..7f2ae0a867 100644 --- a/site/gatsby-site/cypress/e2e/integration/pages.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/pages.cy.js @@ -54,7 +54,7 @@ describe('Pages', () => { paths.forEach((path) => { languages.forEach(({ code }) => { - it(`/${code}${path} Should not have errors`, () => { + it(`/${code}${path} Should not have errors`, { defaultCommandTimeout: 30000 }, () => { const canonicalPath = switchLocalizedPath({ newLang: code, path }); cy.visit(canonicalPath, { @@ -118,44 +118,50 @@ describe('Pages', () => { `[rel="alternate"][hrefLang="${language.hrefLang}"][href="${alternateUrl}"]` ).should('exist'); } + }); - cy.get('body') - .then(($body) => { - if ($body.find('[data-cy="cloudinary-image-wrapper"]').length) { - return true; - } - return false; - }) - .then((selectorExists) => { - if (selectorExists) { - cy.get('[data-cy="cloudinary-image-wrapper"]').each(($el) => { - cy.wrap($el) - .scrollIntoView() - .find('[data-cy="cloudinary-image"]') - .should('have.attr', 'src') - .then(($src) => { - cy.request({ - url: $src.toString(), - failOnStatusCode: false, - }).then((resp) => { - if (resp.status !== 200) { - // If image is failing, check if cloudinary image is hidden - cy.wrap($el) - .find('[data-cy="cloudinary-image"]') - .should('have.class', 'hidden'); - // Check if placeholder image is displayed instead - cy.wrap($el) - .find('[data-cy="cloudinary-image-placeholder"]') - .should('not.have.class', 'hidden'); - } + it.skip( + `/${code}${path} Should load images properly`, + { defaultCommandTimeout: 30000 }, + () => { + cy.get('body') + .then(($body) => { + if ($body.find('[data-cy="cloudinary-image-wrapper"]').length) { + return true; + } + return false; + }) + .then((selectorExists) => { + if (selectorExists) { + cy.get('[data-cy="cloudinary-image-wrapper"]').each(($el) => { + cy.wrap($el) + .scrollIntoView() + .find('[data-cy="cloudinary-image"]') + .should('have.attr', 'src') + .then(($src) => { + cy.request({ + url: $src.toString(), + failOnStatusCode: false, + }).then((resp) => { + if (resp.status !== 200) { + // If image is failing, check if cloudinary image is hidden + cy.wrap($el) + .find('[data-cy="cloudinary-image"]') + .should('have.class', 'hidden'); + // Check if placeholder image is displayed instead + cy.wrap($el) + .find('[data-cy="cloudinary-image-placeholder"]') + .should('not.have.class', 'hidden'); + } + }); }); - }); - }); - } else { - cy.log(`No images found on page, skipping image test for path ${path}`); - } - }); - }); + }); + } else { + cy.log(`No images found on page, skipping image test for path ${path}`); + } + }); + } + ); }); }); }); diff --git a/site/gatsby-site/cypress/e2e/integration/reportHistory.cy.js b/site/gatsby-site/cypress/e2e/integration/reportHistory.cy.js index b07445c993..c30743d944 100644 --- a/site/gatsby-site/cypress/e2e/integration/reportHistory.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/reportHistory.cy.js @@ -136,7 +136,7 @@ describe('Report History', () => { cy.url().should('include', '/reports/3206'); }); - conditionalIt( + conditionalIt.skip( !Cypress.env('isEmptyEnvironment'), 'Should refresh Report history if the user go back on the browser', () => { @@ -167,7 +167,9 @@ describe('Report History', () => { cy.go('forward'); - cy.wait('@FindReportHistory'); + cy.waitForStableDOM(); + + cy.wait('@FindReportHistory', { timeout: 80000 }); } ); diff --git a/site/gatsby-site/cypress/e2e/integration/snapshots.cy.js b/site/gatsby-site/cypress/e2e/integration/snapshots.cy.js new file mode 100644 index 0000000000..6064bb9895 --- /dev/null +++ b/site/gatsby-site/cypress/e2e/integration/snapshots.cy.js @@ -0,0 +1,30 @@ +import { conditionalIt } from '../../support/utils'; + +describe('The Database Snapshots Page', () => { + const url = '/research/snapshots'; + + it('Successfully loads', () => { + cy.visit(url); + }); + + conditionalIt( + !Cypress.env('isEmptyEnvironment'), + 'Should display a list of snapshots to download', + () => { + cy.visit(url); + + cy.get('[data-cy="snapshots-list"] li') + .should('exist') + .and('be.visible') + .and('have.length.gt', 0); + + cy.get('[data-cy="snapshots-list"] li').each((item) => { + expect(item[0].innerText).to.match( + /^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2} (AM|PM) · \d+(\.\d{2})? MB · backup-\d{14}\.tar\.bz2$/ + ); + + expect(item.find('a').attr('href')).to.match(/^https:\/\/.*\/backup-\d{14}\.tar\.bz2$/); + }); + } + ); +}); diff --git a/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js b/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js index 69a9f98307..815c784dff 100644 --- a/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js @@ -26,7 +26,7 @@ describe('Social Share buttons on pages', { retries: { runMode: 4 } }, () => { }); } - urlsToTest.forEach(({ page, url, shareButtonSections }) => { + urlsToTest.forEach(({ page, url, title, shareButtonSections }) => { it(`${page} page should have ${shareButtonSections} Social Share button sections`, () => { cy.visit(url); @@ -38,7 +38,7 @@ describe('Social Share buttons on pages', { retries: { runMode: 4 } }, () => { const canonicalUrl = `https://incidentdatabase.ai${url}`; // Twitter share - it(`${page} page should have a Twitter share button`, () => { + it.skip(`${page} page should have a Twitter share button`, () => { cy.visit(url); cy.get('[data-cy=btn-share-twitter]').should('exist'); @@ -56,9 +56,8 @@ describe('Social Share buttons on pages', { retries: { runMode: 4 } }, () => { cy.get('@popup_twitter', { timeout: 8000 }).should('be.called'); cy.url().should( 'contain', - `https://twitter.com/i/flow/login?redirect_after_login=%2Fintent%2Ftweet%3Ftext%3D` + `https://twitter.com/intent/tweet?text=${encodeURI(title)}&url=${canonicalUrl}` ); - cy.url().should('contain', `url%3D${encodeURIComponent(canonicalUrl)}`); }); // LinkedIn share diff --git a/site/gatsby-site/cypress/e2e/integration/submit.cy.js b/site/gatsby-site/cypress/e2e/integration/submit.cy.js index a232e4f12e..de216e4577 100644 --- a/site/gatsby-site/cypress/e2e/integration/submit.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/submit.cy.js @@ -133,6 +133,7 @@ describe('The Submit form', () => { .contains(/^YouTube$/) .click(); + cy.get('input[name="deployers"]').type('NewDeployer{enter}'); cy.get('button[type="submit"]').click(); cy.wait('@insertSubmission').then((xhr) => { @@ -148,7 +149,7 @@ describe('The Submit form', () => { "Recent news stories and blog\n\nposts highlighted the underbelly of YouTube Kids, Google's children-friendly version. This is more text to reach the 256 charactrs minimum, becuase otherwise the text by similarity component doesnt fetch, which surprisingly is way more character that I initially imagined when I started writing this.\n", url: `https://www.arstechnica.com/gadgets/2017/11/youtube-to-crack-down-on-inappropriate-content-masked-as-kids-cartoons/`, source_domain: `arstechnica.com`, - deployers: { link: ['youtube'] }, + deployers: { link: ['youtube', 'newdeployer'] }, }); }); diff --git a/site/gatsby-site/cypress/e2e/unit/AlgoliaUpdater.cy.js b/site/gatsby-site/cypress/e2e/unit/AlgoliaUpdater.cy.js index 4eecab3ec1..ffb0626d16 100644 --- a/site/gatsby-site/cypress/e2e/unit/AlgoliaUpdater.cy.js +++ b/site/gatsby-site/cypress/e2e/unit/AlgoliaUpdater.cy.js @@ -59,19 +59,41 @@ const reports = [ title: 'Report 23 title', url: 'https://url.com/stuff', }, + + // this report hast no parent incidents + { + _id: new ObjectID('60dd465f80935bc89e6f9b07'), + authors: ['Test User'], + date_downloaded: '2019-04-13', + date_modified: '2020-06-14', + date_published: '2015-05-19', + date_submitted: '2019-06-01', + description: 'Description of report 40', + epoch_date_downloaded: 1555113600, + epoch_date_modified: 1592092800, + epoch_date_published: 1431993600, + epoch_date_submitted: 1559347200, + image_url: 'http://url.com', + cloudinary_id: 'http://cloudinary.com', + language: 'es', + report_number: 40, + source_domain: 'blogs.wsj.com', + submitters: ['Roman Yampolskiy'], + tags: [], + text: 'Report 40 **text**', + plain_text: 'Report 40 text', + title: 'Report 40 title', + url: 'https://url.com/stuff', + }, ]; const classifications = [ { _id: '60dd465f80935bc89e6f9b00', incidents: [1], + reports: [], namespace: 'CSETv0', attributes: [ - // { short_name: 'Annotator', value_json: '"1"' }, - // { short_name: 'Annotation Status', value_json: '"6. Complete and final"' }, - // { short_name: 'Reviewer', value_json: '"5"' }, - // { short_name: 'Quality Control', value_json: 'false' }, - // { short_name: 'Full Description', value_json: '"On December 5, 2018, a robot punctured."' }, { short_name: 'Named Entities', value_json: '["Amazon"]' }, { short_name: 'Harm Type', @@ -83,7 +105,8 @@ const classifications = [ }, { _id: '60dd465f80935bc89e6f9b01', - incidents: [1], + incidents: [], + reports: [], namespace: 'SHOULD NOT BE INCLUDED', attributes: [{ short_name: 'Something', value_json: '"Great"' }], notes: 'Nothing to see here', @@ -265,7 +288,11 @@ describe('Algolia', () => { }); cy.wrap(updater.run()).then(() => { - expect(mongoClient.connect.callCount).to.eq(4); + expect(mongoClient.connect.callCount).to.eq(1); + + // english + + expect(enIndex.replaceAllObjects.getCalls().length).eq(1); expect(enIndex.replaceAllObjects.getCall(0).args[0].length).eq(2); @@ -290,11 +317,16 @@ describe('Algolia', () => { incident_id: 1, epoch_incident_date: 1592092800, incident_date: '2020-06-14', + namespaces: ['CSETv0'], classifications: [ 'CSETv0:Named Entities:Amazon', 'CSETv0:Harm Type:Harm to physical health/safety', 'CSETv0:Harm Type:Harm to physical property', ], + CSETv0: { + 'Named Entities': ['Amazon'], + 'Harm Type': ['Harm to physical health/safety', 'Harm to physical property'], + }, }); expect(enIndex.replaceAllObjects.getCall(0).args[0][1]).to.deep.nested.include({ @@ -318,13 +350,29 @@ describe('Algolia', () => { incident_id: 1, incident_date: '2020-06-14', epoch_incident_date: 1592092800, + namespaces: ['CSETv0'], classifications: [ 'CSETv0:Named Entities:Amazon', 'CSETv0:Harm Type:Harm to physical health/safety', 'CSETv0:Harm Type:Harm to physical property', ], + CSETv0: { + 'Named Entities': ['Amazon'], + 'Harm Type': ['Harm to physical health/safety', 'Harm to physical property'], + }, + }); + + expect(enIndex.deleteBy.getCall(0).args[0]).deep.eq({ + filters: 'incident_id = 247', }); + ``; + // spanish + + expect(esIndex.replaceAllObjects.getCalls().length).eq(1); + + expect(esIndex.replaceAllObjects.getCall(0).args[0].length).eq(2); + expect(esIndex.replaceAllObjects.getCall(0).args[0][0]).to.deep.nested.include({ authors: ['Alistair Barr'], description: 'Description of report 1', @@ -346,11 +394,16 @@ describe('Algolia', () => { incident_id: 1, incident_date: '2020-06-14', epoch_incident_date: 1592092800, + namespaces: ['CSETv0'], classifications: [ 'CSETv0:Named Entities:Amazon', 'CSETv0:Harm Type:Harm to physical health/safety', 'CSETv0:Harm Type:Harm to physical property', ], + CSETv0: { + 'Named Entities': ['Amazon'], + 'Harm Type': ['Harm to physical health/safety', 'Harm to physical property'], + }, featured: 0, }); @@ -375,23 +428,24 @@ describe('Algolia', () => { incident_id: 1, incident_date: '2020-06-14', epoch_incident_date: 1592092800, + namespaces: ['CSETv0'], classifications: [ 'CSETv0:Named Entities:Amazon', 'CSETv0:Harm Type:Harm to physical health/safety', 'CSETv0:Harm Type:Harm to physical property', ], + CSETv0: { + 'Named Entities': ['Amazon'], + 'Harm Type': ['Harm to physical health/safety', 'Harm to physical property'], + }, featured: 2, }); - expect(enIndex.deleteBy.getCall(0).args[0]).deep.eq({ - filters: 'incident_id = 247', - }); - expect(esIndex.deleteBy.getCall(0).args[0]).deep.eq({ filters: 'incident_id = 247', }); - expect(mongoClient.close.callCount).to.eq(4); + expect(mongoClient.close.callCount).to.eq(1); }); }); }); diff --git a/site/gatsby-site/cypress/e2e/unit/discover/routing.cy.js b/site/gatsby-site/cypress/e2e/unit/discover/routing.cy.js index 518f13dfdd..aa41c27837 100644 --- a/site/gatsby-site/cypress/e2e/unit/discover/routing.cy.js +++ b/site/gatsby-site/cypress/e2e/unit/discover/routing.cy.js @@ -23,7 +23,7 @@ describe('Discover routing', () => { expect(state.refinementList).to.deep.eq({ source_domain: ['theguardian.com'], authors: ['Christopher Knaus', 'Sam Levin'], - classifications: ['CSETv0:Intent:Accident'], + 'CSETv0.Intent': ['Accident'], is_incident_report: ['true'], }); expect(state.range).to.deep.eq({ @@ -40,6 +40,7 @@ describe('Discover routing', () => { indexName, locale: 'en', queryConfig, + taxa: ['CSETv0', 'CSETv1'], }); expect('?' + resultURL).to.eq(location.search); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications.cy.js deleted file mode 100644 index fea695c20b..0000000000 --- a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications.cy.js +++ /dev/null @@ -1,841 +0,0 @@ -const { SUBSCRIPTION_TYPE } = require('../../../../src/utils/subscriptions'); - -const processNotifications = require('../../../../../realm/functions/processNotifications'); - -const pendingNotificationsToNewIncidents = [ - { - _id: '63616f37d0db19c07d081300', - type: SUBSCRIPTION_TYPE.newIncidents, - incident_id: 217, - processed: false, - }, - { - _id: '63616f82d0db19c07d081301', - type: SUBSCRIPTION_TYPE.newIncidents, - incident_id: 218, - processed: false, - }, -]; - -const pendingNotificationsToNewEntityIncidents = [ - { - _id: '63616f82d0db19c07d081302', - type: SUBSCRIPTION_TYPE.entity, - incident_id: 219, - entity_id: 'google', - processed: false, - }, - { - _id: '63616f82d0db19c07d081303', - type: SUBSCRIPTION_TYPE.entity, - incident_id: 219, - entity_id: 'facebook', - isUpdate: true, - processed: false, - }, -]; - -const pendingNotificationsToIncidentUpdates = [ - { - _id: '63616f82d0db19c07d081304', - type: 'incident-updated', - incident_id: 219, - processed: false, - }, - { - _id: '63616f82d0db19c07d081305', - type: 'new-report-incident', - incident_id: 219, - report_number: 2000, - processed: false, - }, -]; - -const pendingNotificationsToPromotedIncidents = [ - { - _id: '63616f82d0db19c07d081306', - type: SUBSCRIPTION_TYPE.submissionPromoted, - incident_id: 217, - processed: false, - }, -]; - -const subscriptionsToNewIncidents = [ - { - _id: '6356e39e863169c997309586', - type: SUBSCRIPTION_TYPE.newIncidents, - userId: '63320ce63ec803072c9f5291', - }, - { - _id: '6356e39e863169c997309586', - type: SUBSCRIPTION_TYPE.newIncidents, - userId: '63321072f27421740a80af22', - }, -]; - -const subscriptionsToNewEntityIncidents = [ - { - _id: '6356e39e863169c997309586', - type: SUBSCRIPTION_TYPE.entity, - entityId: 'google', - userId: '63321072f27421740a80af23', - }, - { - _id: '6356e39e863169c997309586', - type: SUBSCRIPTION_TYPE.entity, - entityId: 'facebook', - userId: '63321072f27421740a80af24', - }, -]; - -const subscriptionsToIncidentUpdates = [ - { - userId: '63320ce63ec803072c9f5291', - type: SUBSCRIPTION_TYPE.incident, - incident_id: 219, - }, - { - userId: '63321072f27421740a80af22', - type: SUBSCRIPTION_TYPE.incident, - incident_id: 219, - }, -]; - -const subscriptionsToPromotedIncidents = [ - { - _id: '6356e39e863169c997309586', - type: SUBSCRIPTION_TYPE.submissionPromoted, - userId: '63320ce63ec803072c9f5291', - }, -]; - -const recipients = [ - { - email: 'test1@email.com', - userId: '63320ce63ec803072c9f5291', - }, - { - email: 'test2@email.com', - userId: '63321072f27421740a80af22', - }, - { - email: 'test3@email.com', - userId: '63321072f27421740a80af23', - }, - { - email: 'test4@email.com', - userId: '63321072f27421740a80af24', - }, -]; - -const incidents = [ - { - incident_id: 217, - 'Alleged developer of AI system': [], - 'Alleged deployer of AI system': [], - 'Alleged harmed or nearly harmed parties': [], - AllegedDeployerOfAISystem: [], - AllegedDeveloperOfAISystem: [], - AllegedHarmedOrNearlyHarmedParties: [], - __typename: 'Incident', - date: '2018-11-16', - description: 'Twenty-four Amazon workers in New Jersey were hospitalized.', - nlp_similar_incidents: [], - reports: [1, 2], - title: '217 Amazon workers sent to hospital', - }, - { - incident_id: 218, - 'Alleged developer of AI system': [], - 'Alleged deployer of AI system': [], - 'Alleged harmed or nearly harmed parties': [], - __typename: 'Incident', - date: '2018-11-16', - description: 'Twenty-four Amazon workers in New Jersey were hospitalized.', - nlp_similar_incidents: [], - reports: [1, 2], - title: '218 Amazon workers sent to hospital', - }, - { - incident_id: 219, - 'Alleged developer of AI system': ['google', 'facebook'], - 'Alleged deployer of AI system': ['facebook'], - 'Alleged harmed or nearly harmed parties': ['tesla'], - __typename: 'Incident', - date: '2018-11-16', - description: 'Twenty-four Amazon workers in New Jersey were hospitalized.', - nlp_similar_incidents: [], - reports: [1, 2, 2000], - title: '218 Amazon workers sent to hospital', - }, -]; - -const reports = [ - { - report_number: 2000, - title: 'Report title', - authors: ['Pablo Costa', 'Aimee Picchi'], - }, -]; - -const entities = [ - { - entity_id: 'google', - name: 'Google', - }, - { - entity_id: 'facebook', - name: 'Facebook', - }, - { - entity_id: 'boston-university', - name: 'Boston University', - }, -]; - -const buildEntityList = (allEntities, entityIds) => { - const entityNames = entityIds.map((entityId) => { - const entity = allEntities.find((entity) => entity.entity_id === entityId); - - return entity - ? `${entity.name}` - : ''; - }); - - if (entityNames.length < 3) { - return entityNames.join(' and '); - } - - return `${entityNames.slice(0, -1).join(', ')}, and ${entityNames[entityNames.length - 1]}`; -}; - -const stubEverything = () => { - const notificationsCollection = { - find: (() => { - const stub = cy.stub(); - - stub - .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.newIncidents }) - .as(`notifications.find(${SUBSCRIPTION_TYPE.newIncidents})`) - .returns({ toArray: () => pendingNotificationsToNewIncidents }); - - stub - .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.entity }) - .as(`notifications.find(${SUBSCRIPTION_TYPE.entity})`) - .returns({ toArray: () => pendingNotificationsToNewEntityIncidents }); - - stub - .withArgs({ processed: false, type: { $in: ['new-report-incident', 'incident-updated'] } }) - .as(`notifications.find('new-report-incident', 'incident-updated')`) - .returns({ toArray: () => pendingNotificationsToIncidentUpdates }); - - stub - .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.submissionPromoted }) - .as(`notifications.find(${SUBSCRIPTION_TYPE.submissionPromoted})`) - .returns({ toArray: () => pendingNotificationsToPromotedIncidents }); - - return stub; - })(), - updateOne: cy.stub().as('notifications.updateOne').resolves(), - }; - - const subscriptionsCollection = { - find: (() => { - const stub = cy.stub(); - - stub - .withArgs({ type: SUBSCRIPTION_TYPE.newIncidents }) - .as(`subscriptions.find("${SUBSCRIPTION_TYPE.newIncidents}")`) - .returns({ toArray: () => subscriptionsToNewIncidents }); - - for (const pendingNotification of pendingNotificationsToNewEntityIncidents) { - stub - .withArgs({ type: SUBSCRIPTION_TYPE.entity, entityId: pendingNotification.entity_id }) - .as( - `subscriptions.find("${SUBSCRIPTION_TYPE.entity}", "${pendingNotification.entity_id}")` - ) - .returns({ toArray: () => subscriptionsToNewEntityIncidents }); - } - - for (const pendingNotification of pendingNotificationsToIncidentUpdates) { - stub - .withArgs({ - type: SUBSCRIPTION_TYPE.incident, - incident_id: pendingNotification.incident_id, - }) - .as( - `subscriptions.find("${SUBSCRIPTION_TYPE.incident}", "${pendingNotification.incident_id}")` - ) - .returns({ toArray: () => subscriptionsToIncidentUpdates }); - } - - const incidentIds = pendingNotificationsToPromotedIncidents.map( - (pendingNotification) => pendingNotification.incident_id - ); - - stub - .withArgs({ - type: SUBSCRIPTION_TYPE.submissionPromoted, - incident_id: { $in: incidentIds }, - }) - .as(`subscriptions.find("${SUBSCRIPTION_TYPE.submissionPromoted}")`) - .returns({ toArray: () => subscriptionsToPromotedIncidents }); - - return stub; - })(), - }; - - const incidentsCollection = { - findOne: (() => { - const stub = cy.stub(); - - for (let index = 0; index < incidents.length; index++) { - const incident = incidents[index]; - - stub - .withArgs({ incident_id: incident.incident_id }) - .as(`incidents.findOne(${incident.incident_id})`) - .returns(incidents.find((i) => i.incident_id == incident.incident_id)); - } - - return stub; - })(), - }; - - const reportsCollection = { - findOne: (() => { - const stub = cy.stub(); - - for (let index = 0; index < reports.length; index++) { - const report = reports[index]; - - stub - .withArgs({ report_number: report.report_number }) - .as(`reports.findOne(${report.report_number})`) - .returns(reports.find((r) => r.report_number == report.report_number)); - } - - return stub; - })(), - }; - - const entitiesCollection = { - find: cy.stub().returns({ - toArray: cy.stub().as('entities.find').resolves(entities), - }), - }; - - global.context = { - // @ts-ignore - services: { - get: cy.stub().returns({ - db: cy.stub().returns({ - collection: (() => { - const stub = cy.stub(); - - stub.withArgs('notifications').returns(notificationsCollection); - stub.withArgs('subscriptions').returns(subscriptionsCollection); - stub.withArgs('incidents').returns(incidentsCollection); - stub.withArgs('entities').returns(entitiesCollection); - stub.withArgs('reports').returns(reportsCollection); - - return stub; - })(), - }), - }), - }, - functions: { - execute: (() => { - const stub = cy.stub(); - - for (const user of recipients) { - stub - .withArgs('getUser', { userId: user.userId }) - .as(`getUser(${user.userId})`) - .returns(recipients.find((r) => r.userId == user.userId)); - } - - stub.withArgs('sendEmail').as('sendEmail').returns({ statusCode: 200 }); - - return stub; - })(), - }, - }; - - global.BSON = { Int32: (x) => x }; - - return { - notificationsCollection, - subscriptionsCollection, - incidentsCollection, - entitiesCollection, - reportsCollection, - }; -}; - -describe('Functions', () => { - it('New Incidents - Should send pending notifications', () => { - const { notificationsCollection, subscriptionsCollection, incidentsCollection } = - stubEverything(); - - cy.wrap(processNotifications()).then((result) => { - expect(result, 'Notifications processed count').to.be.equal(7); - - expect(notificationsCollection.find.firstCall.args[0]).to.deep.equal({ - processed: false, - type: SUBSCRIPTION_TYPE.newIncidents, - }); - - expect(subscriptionsCollection.find.firstCall.args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.newIncidents, - }); - - for (const subscription of subscriptionsToNewIncidents) { - expect(global.context.functions.execute).to.be.calledWith('getUser', { - userId: subscription.userId, - }); - } - - for (let i = 0; i < pendingNotificationsToNewIncidents.length; i++) { - const pendingNotification = pendingNotificationsToNewIncidents[i]; - - expect(incidentsCollection.findOne.getCall(i).args[0]).to.deep.equal({ - incident_id: pendingNotification.incident_id, - }); - - const userIds = subscriptionsToNewIncidents.map((subscription) => subscription.userId); - - const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); - - const sendEmailParams = { - recipients: recipients.filter((r) => userIds.includes(r.userId)), - subject: 'New Incident {{incidentId}} was created', - dynamicData: { - incidentId: `${incident.incident_id}`, - incidentTitle: incident.title, - incidentUrl: `https://incidentdatabase.ai/cite/${pendingNotification.incident_id}`, - incidentDescription: incident.description, - incidentDate: incident.date, - developers: buildEntityList(entities, incident['Alleged developer of AI system']), - deployers: buildEntityList(entities, incident['Alleged deployer of AI system']), - entitiesHarmed: buildEntityList( - entities, - incident['Alleged harmed or nearly harmed parties'] - ), - }, - templateId: 'NewIncident', // Template value from function name sufix from "site/realm/functions/config.json" - }; - - expect(global.context.functions.execute).to.be.calledWith('sendEmail', sendEmailParams); - - expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ - _id: pendingNotification._id, - }); - - expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( - true - ); - expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( - 'sentDate' - ); - } - }); - }); - - it('New Promotions - Should send pending submissions promoted notifications', () => { - const { notificationsCollection, subscriptionsCollection, incidentsCollection } = - stubEverything(); - - cy.wrap(processNotifications()).then((result) => { - expect(result, 'Notifications processed count').to.be.equal(7); - expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ - processed: false, - type: SUBSCRIPTION_TYPE.submissionPromoted, - }); - - expect(subscriptionsCollection.find.getCall(5).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.submissionPromoted, - incident_id: { $in: [217] }, - }); - - for (const subscription of subscriptionsToPromotedIncidents) { - expect(global.context.functions.execute).to.be.calledWith('getUser', { - userId: subscription.userId, - }); - } - - for (let i = 0; i < pendingNotificationsToPromotedIncidents.length; i++) { - const pendingNotification = pendingNotificationsToPromotedIncidents[i]; - - expect(incidentsCollection.findOne.getCall(i).args[0]).to.deep.equal({ - incident_id: pendingNotification.incident_id, - }); - - const userIds = subscriptionsToPromotedIncidents.map((subscription) => subscription.userId); - - const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); - - const sendEmailParams = { - recipients: recipients.filter((r) => userIds.includes(r.userId)), - subject: 'Your submission has been approved!', - dynamicData: { - incidentId: `${incident.incident_id}`, - incidentTitle: incident.title, - incidentUrl: `https://incidentdatabase.ai/cite/${pendingNotification.incident_id}`, - incidentDescription: incident.description, - incidentDate: incident.date, - }, - templateId: 'SubmissionApproved', // Template value from function name sufix from "site/realm/functions/config.json" - }; - - expect(global.context.functions.execute).to.be.calledWith('sendEmail', sendEmailParams); - - expect(notificationsCollection.updateOne.getCall(6).args[0]).to.deep.equal({ - _id: pendingNotification._id, - }); - - expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( - true - ); - expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( - 'sentDate' - ); - } - }); - }); - - it('Entity - Should send pending notifications', () => { - const { - notificationsCollection, - subscriptionsCollection, - incidentsCollection, - entitiesCollection, - } = stubEverything(); - - cy.wrap(processNotifications()).then((result) => { - expect(result, 'Notifications processed count').to.be.equal(7); - - expect(notificationsCollection.find.secondCall.args[0]).to.deep.equal({ - processed: false, - type: SUBSCRIPTION_TYPE.entity, - }); - - expect(entitiesCollection.find.firstCall.args[0]).to.deep.equal({}); - - for (let i = 0; i < pendingNotificationsToNewEntityIncidents.length; i++) { - const pendingNotification = pendingNotificationsToNewEntityIncidents[i]; - - expect(subscriptionsCollection.find.getCall(i + 1).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.entity, - entityId: pendingNotification.entity_id, - }); - - for (const subscription of subscriptionsToNewEntityIncidents) { - expect(global.context.functions.execute).to.be.calledWith('getUser', { - userId: subscription.userId, - }); - } - - expect( - incidentsCollection.findOne.getCall(pendingNotificationsToNewIncidents.length + i).args[0] - ).to.deep.equal({ - incident_id: pendingNotification.incident_id, - }); - - const userIds = subscriptionsToNewEntityIncidents.map( - (subscription) => subscription.userId - ); - - const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); - - const entity = entities.find( - (entity) => entity.entity_id === pendingNotification.entity_id - ); - - const isIncidentUpdate = pendingNotification.isUpdate; - - const sendEmailParams = { - recipients: recipients.filter((r) => userIds.includes(r.userId)), - subject: isIncidentUpdate - ? 'Update Incident for {{entityName}}' - : 'New Incident for {{entityName}}', - dynamicData: { - incidentId: `${incident.incident_id}`, - incidentTitle: incident.title, - incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, - incidentDescription: incident.description, - incidentDate: incident.date, - entityName: entity.name, - entityUrl: `https://incidentdatabase.ai/entities/${entity.entity_id}`, - developers: buildEntityList(entities, incident['Alleged developer of AI system']), - deployers: buildEntityList(entities, incident['Alleged deployer of AI system']), - entitiesHarmed: buildEntityList( - entities, - incident['Alleged harmed or nearly harmed parties'] - ), - }, - // Template value from function name sufix from "site/realm/functions/config.json" - templateId: isIncidentUpdate ? 'EntityIncidentUpdated' : 'NewEntityIncident', - }; - - expect(global.context.functions.execute).to.be.calledWith('sendEmail', sendEmailParams); - - expect( - notificationsCollection.updateOne.getCall(pendingNotificationsToNewIncidents.length + i) - .args[0] - ).to.deep.equal({ - _id: pendingNotification._id, - }); - - expect( - notificationsCollection.updateOne.getCall(pendingNotificationsToNewIncidents.length + i) - .args[1].$set.processed - ).to.be.equal(true); - expect( - notificationsCollection.updateOne.getCall(pendingNotificationsToNewIncidents.length + i) - .args[1].$set - ).to.have.ownProperty('sentDate'); - } - }); - }); - - it('Incident Updated - Should send pending notifications', () => { - const { - notificationsCollection, - subscriptionsCollection, - incidentsCollection, - entitiesCollection, - } = stubEverything(); - - cy.wrap(processNotifications()).then((result) => { - expect(result, 'Notifications processed count').to.be.equal(7); - - expect(notificationsCollection.find.secondCall.args[0]).to.deep.equal({ - processed: false, - type: SUBSCRIPTION_TYPE.entity, - }); - - expect(entitiesCollection.find.firstCall.args[0]).to.deep.equal({}); - - for (let i = 0; i < pendingNotificationsToIncidentUpdates.length; i++) { - const pendingNotification = pendingNotificationsToIncidentUpdates[i]; - - expect(subscriptionsCollection.find.getCall(i + 3).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.incident, - incident_id: pendingNotification.incident_id, - }); - - for (const subscription of subscriptionsToIncidentUpdates) { - expect(global.context.functions.execute).to.be.calledWith('getUser', { - userId: subscription.userId, - }); - } - - expect(incidentsCollection.findOne.getCall(i + 4).args[0]).to.deep.equal({ - incident_id: pendingNotification.incident_id, - }); - - const userIds = subscriptionsToIncidentUpdates.map((subscription) => subscription.userId); - - const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); - - const newReportNumber = pendingNotification.report_number; - - const newReport = newReportNumber - ? reports.find((r) => r.report_number == pendingNotification.report_number) - : null; - - const sendEmailParams = { - recipients: recipients.filter((r) => userIds.includes(r.userId)), - subject: 'Incident {{incidentId}} was updated', - dynamicData: { - incidentId: `${incident.incident_id}`, - incidentTitle: incident.title, - incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, - reportUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}#r${newReportNumber}`, - reportTitle: newReportNumber ? newReport.title : '', - reportAuthor: newReportNumber && newReport.authors[0] ? newReport.authors[0] : '', - }, - templateId: newReportNumber // Template value from function name sufix from "site/realm/functions/config.json" - ? 'NewReportAddedToAnIncident' - : 'IncidentUpdate', - }; - - expect(global.context.functions.execute).to.be.calledWith('sendEmail', sendEmailParams); - - expect(notificationsCollection.updateOne.getCall(i + 4).args[0]).to.deep.equal({ - _id: pendingNotification._id, - }); - expect(notificationsCollection.updateOne.getCall(i + 4).args[1].$set.processed).to.be.equal( - true - ); - expect(notificationsCollection.updateOne.getCall(i + 4).args[1].$set).to.have.ownProperty( - 'sentDate' - ); - } - }); - }); - - it('Should mark pending notifications as processed if there are no subscribers', () => { - const notificationsCollection = { - find: (() => { - const stub = cy.stub(); - - stub - .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.newIncidents }) - .as(`notifications.find(${SUBSCRIPTION_TYPE.newIncidents})`) - .returns({ toArray: () => pendingNotificationsToNewIncidents }); - - stub - .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.entity }) - .as(`notifications.find(${SUBSCRIPTION_TYPE.entity})`) - .returns({ toArray: () => pendingNotificationsToNewEntityIncidents }); - - stub - .withArgs({ - processed: false, - type: { $in: ['new-report-incident', 'incident-updated'] }, - }) - .as(`notifications.find('new-report-incident', 'incident-updated')`) - .returns({ toArray: () => pendingNotificationsToIncidentUpdates }); - - stub - .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.submissionPromoted }) - .as(`notifications.find(${SUBSCRIPTION_TYPE.submissionPromoted})`) - .returns({ toArray: () => pendingNotificationsToPromotedIncidents }); - - return stub; - })(), - updateOne: cy.stub().as('notifications.updateOne').resolves(), - }; - - const subscriptionsCollection = { - find: cy - .stub() - .as('subscriptions.find') - .returns({ - toArray: cy.stub().as('toArray').resolves([]), - }), - }; - - const entitiesCollection = { - find: cy - .stub() - .as('entities.find') - .returns({ - toArray: cy.stub().as('toArray').resolves(entities), - }), - }; - - global.context = { - // @ts-ignore - services: { - get: cy.stub().returns({ - db: cy.stub().returns({ - collection: (() => { - const stub = cy.stub(); - - stub.withArgs('notifications').returns(notificationsCollection); - stub.withArgs('subscriptions').returns(subscriptionsCollection); - stub.withArgs('entities').returns(entitiesCollection); - - return stub; - })(), - }), - }), - }, - functions: { - execute: cy.stub().as('functions.execute').resolves(), - }, - }; - - global.BSON = { Int32: (x) => x }; - - cy.wrap(processNotifications()).then((result) => { - expect(result, 'Notifications processed count').to.be.equal(7); - - expect(notificationsCollection.find.getCall(0).args[0]).to.deep.equal({ - processed: false, - type: SUBSCRIPTION_TYPE.newIncidents, - }); - - expect(notificationsCollection.find.getCall(1).args[0]).to.deep.equal({ - processed: false, - type: SUBSCRIPTION_TYPE.entity, - }); - - expect(notificationsCollection.find.getCall(2).args[0]).to.deep.equal({ - processed: false, - type: { $in: ['new-report-incident', 'incident-updated'] }, - }); - - expect(subscriptionsCollection.find.getCall(0).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.newIncidents, - }); - - expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ - processed: false, - type: SUBSCRIPTION_TYPE.submissionPromoted, - }); - - expect(global.context.functions.execute).not.to.be.called; - - for (let i = 0; i < pendingNotificationsToNewIncidents.length; i++) { - const pendingNotification = pendingNotificationsToNewIncidents[i]; - - expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ - _id: pendingNotification._id, - }); - expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( - true - ); - expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( - 'sentDate' - ); - } - - for (let i = 0; i < pendingNotificationsToNewEntityIncidents.length; i++) { - const pendingNotification = pendingNotificationsToNewEntityIncidents[i]; - - expect(subscriptionsCollection.find.getCall(i + 1).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.entity, - entityId: pendingNotification.entity_id, - }); - - expect(notificationsCollection.updateOne.getCall(i + 2).args[0]).to.deep.equal({ - _id: pendingNotification._id, - }); - expect(notificationsCollection.updateOne.getCall(i + 2).args[1].$set.processed).to.be.equal( - true - ); - expect(notificationsCollection.updateOne.getCall(i + 2).args[1].$set).to.have.ownProperty( - 'sentDate' - ); - } - - for (let i = 0; i < pendingNotificationsToIncidentUpdates.length; i++) { - const pendingNotification = pendingNotificationsToIncidentUpdates[i]; - - expect(subscriptionsCollection.find.getCall(i + 3).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.incident, - incident_id: pendingNotification.incident_id, - }); - - expect(notificationsCollection.updateOne.getCall(i + 4).args[0]).to.deep.equal({ - _id: pendingNotification._id, - }); - expect(notificationsCollection.updateOne.getCall(i + 4).args[1].$set.processed).to.be.equal( - true - ); - expect(notificationsCollection.updateOne.getCall(i + 4).args[1].$set).to.have.ownProperty( - 'sentDate' - ); - } - - expect( - notificationsCollection.updateOne.getCalls().length, - 'Notifications marked as processed count' - ).to.be.equal(7); - }); - }); -}); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/fixtures.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/fixtures.js new file mode 100644 index 0000000000..f4f32d3686 --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/fixtures.js @@ -0,0 +1,83 @@ +export const entities = [ + { + entity_id: 'google', + name: 'Google', + }, + { + entity_id: 'facebook', + name: 'Facebook', + }, + { + entity_id: 'boston-university', + name: 'Boston University', + }, +]; + +export const incidents = [ + { + incident_id: 217, + 'Alleged developer of AI system': [], + 'Alleged deployer of AI system': [], + 'Alleged harmed or nearly harmed parties': [], + AllegedDeployerOfAISystem: [], + AllegedDeveloperOfAISystem: [], + AllegedHarmedOrNearlyHarmedParties: [], + __typename: 'Incident', + date: '2018-11-16', + description: 'Twenty-four Amazon workers in New Jersey were hospitalized.', + nlp_similar_incidents: [], + reports: [1, 2], + title: '217 Amazon workers sent to hospital', + }, + { + incident_id: 218, + 'Alleged developer of AI system': [], + 'Alleged deployer of AI system': [], + 'Alleged harmed or nearly harmed parties': [], + __typename: 'Incident', + date: '2018-11-16', + description: 'Twenty-four Amazon workers in New Jersey were hospitalized.', + nlp_similar_incidents: [], + reports: [1, 2], + title: '218 Amazon workers sent to hospital', + }, + { + incident_id: 219, + 'Alleged developer of AI system': ['google', 'facebook'], + 'Alleged deployer of AI system': ['facebook'], + 'Alleged harmed or nearly harmed parties': ['tesla'], + __typename: 'Incident', + date: '2018-11-16', + description: 'Twenty-four Amazon workers in New Jersey were hospitalized.', + nlp_similar_incidents: [], + reports: [1, 2, 2000], + title: '218 Amazon workers sent to hospital', + }, +]; + +export const reports = [ + { + report_number: 2000, + title: 'Report title', + authors: ['Pablo Costa', 'Aimee Picchi'], + }, +]; + +export const recipients = [ + { + email: 'test1@email.com', + userId: '63320ce63ec803072c9f5291', + }, + { + email: 'test2@email.com', + userId: '63321072f27421740a80af22', + }, + { + email: 'test3@email.com', + userId: '63321072f27421740a80af23', + }, + { + email: 'test4@email.com', + userId: '63321072f27421740a80af24', + }, +]; diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processEntityNotifications.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processEntityNotifications.cy.js new file mode 100644 index 0000000000..acbda32fee --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processEntityNotifications.cy.js @@ -0,0 +1,285 @@ +import { buildEntityList, stubEverything } from './processNotificationsUtils'; + +const { SUBSCRIPTION_TYPE } = require('../../../../../src/utils/subscriptions'); + +const processNotifications = require('../../../../../../realm/functions/processNotifications'); + +const { recipients, entities, incidents } = require('./fixtures'); + +const pendingNotifications = [ + { + _id: '63616f82d0db19c07d081200', + type: SUBSCRIPTION_TYPE.entity, + incident_id: 219, + entity_id: 'google', + processed: false, + }, + { + _id: '63616f82d0db19c07d081201', + type: SUBSCRIPTION_TYPE.entity, + incident_id: 219, + entity_id: 'facebook', + isUpdate: true, + processed: false, + }, + //Duplicated pending notification + { + _id: '63616f82d0db19c07d081202', + type: SUBSCRIPTION_TYPE.entity, + incident_id: 219, + entity_id: 'facebook', + isUpdate: true, + processed: false, + }, + { + _id: '63616f82d0db19c07d081203', + type: SUBSCRIPTION_TYPE.entity, + incident_id: 219, + entity_id: 'google', + processed: false, + }, +]; + +const uniquePendingNotifications = pendingNotifications.slice(0, 2); + +const subscriptions = [ + { + _id: '6356e39e863169c997309586', + type: SUBSCRIPTION_TYPE.entity, + entityId: 'google', + userId: '63321072f27421740a80af23', + }, + { + _id: '6356e39e863169c997309587', + type: SUBSCRIPTION_TYPE.entity, + entityId: 'facebook', + userId: '63321072f27421740a80af24', + }, +]; + +describe('Process Entity Pending Notifications', () => { + it('Entity - Should process all pending notifications', () => { + const { notificationsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.entity, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then((result) => { + expect( + notificationsCollection.updateOne.callCount, + 'Mark notification item as processed' + ).to.be.equal(pendingNotifications.length); + + const sendEmailCalls = global.context.functions.execute + .getCalls() + .filter((call) => call.args[0] === 'sendEmail'); + + expect(sendEmailCalls.length, 'sendEmail function calls').to.be.equal( + uniquePendingNotifications.length + ); + + // Check that the emails are sent only once + for (let i = 0; i < sendEmailCalls.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + const sendEmailCallArgs = sendEmailCalls[i].args[1]; + + const userIds = subscriptions + .filter((s) => s.entityId === pendingNotification.entity_id) + .map((subscription) => subscription.userId); + + const isIncidentUpdate = pendingNotification.isUpdate; + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const entity = entities.find( + (entity) => entity.entity_id === pendingNotification.entity_id + ); + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: isIncidentUpdate + ? 'Update Incident for {{entityName}}' + : 'New Incident for {{entityName}}', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, + incidentDescription: incident.description, + incidentDate: incident.date, + entityName: entity.name, + entityUrl: `https://incidentdatabase.ai/entities/${entity.entity_id}`, + developers: buildEntityList(entities, incident['Alleged developer of AI system']), + deployers: buildEntityList(entities, incident['Alleged deployer of AI system']), + entitiesHarmed: buildEntityList( + entities, + incident['Alleged harmed or nearly harmed parties'] + ), + }, + // Template value from function name sufix from "site/realm/functions/config.json" + templateId: isIncidentUpdate ? 'EntityIncidentUpdated' : 'NewEntityIncident', + }; + + expect(sendEmailCallArgs, 'Send email args').to.be.deep.equal(sendEmailParams); + } + + //No Rollbar error logs + expect( + global.context.functions.execute.getCalls().filter((call) => call.args[0] === 'logRollbar') + .length, + 'logRollbar function calls' + ).to.be.equal(0); + + expect(result, 'Notifications processed count').to.be.equal(pendingNotifications.length); + }); + }); + + it('Entity - Should send pending notifications', () => { + const { + notificationsCollection, + subscriptionsCollection, + incidentsCollection, + entitiesCollection, + } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.entity, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then(() => { + expect(notificationsCollection.find.secondCall.args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.entity, + }); + + expect(entitiesCollection.find.firstCall.args[0]).to.deep.equal({}); + + for (let i = 0; i < uniquePendingNotifications.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + expect(subscriptionsCollection.find.getCall(i).args[0]).to.deep.equal({ + type: SUBSCRIPTION_TYPE.entity, + entityId: pendingNotification.entity_id, + }); + + for (const subscription of subscriptions) { + expect(global.context.functions.execute).to.be.calledWith('getUser', { + userId: subscription.userId, + }); + } + + expect(incidentsCollection.findOne.getCall(i).args[0]).to.deep.equal({ + incident_id: pendingNotification.incident_id, + }); + + const userIds = subscriptions + .filter((s) => s.entityId === pendingNotification.entity_id) + .map((subscription) => subscription.userId); + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const entity = entities.find( + (entity) => entity.entity_id === pendingNotification.entity_id + ); + + const isIncidentUpdate = pendingNotification.isUpdate; + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: isIncidentUpdate + ? 'Update Incident for {{entityName}}' + : 'New Incident for {{entityName}}', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, + incidentDescription: incident.description, + incidentDate: incident.date, + entityName: entity.name, + entityUrl: `https://incidentdatabase.ai/entities/${entity.entity_id}`, + developers: buildEntityList(entities, incident['Alleged developer of AI system']), + deployers: buildEntityList(entities, incident['Alleged deployer of AI system']), + entitiesHarmed: buildEntityList( + entities, + incident['Alleged harmed or nearly harmed parties'] + ), + }, + // Template value from function name sufix from "site/realm/functions/config.json" + templateId: isIncidentUpdate ? 'EntityIncidentUpdated' : 'NewEntityIncident', + }; + + expect(global.context.functions.execute, 'Send email').to.be.calledWith( + 'sendEmail', + sendEmailParams + ); + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + }); + }); + + it('Entity - Should mark pending notifications as processed if there are no subscribers', () => { + const { notificationsCollection, subscriptionsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.entity, + pendingNotifications, + subscriptions: [], + }); + + cy.wrap(processNotifications()).then(() => { + expect(notificationsCollection.find.getCall(0).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.newIncidents, + }); + + expect(notificationsCollection.find.getCall(1).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.entity, + }); + + expect(notificationsCollection.find.getCall(2).args[0]).to.deep.equal({ + processed: false, + type: { $in: ['new-report-incident', 'incident-updated'] }, + }); + + expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.submissionPromoted, + }); + + expect(global.context.functions.execute).not.to.be.called; + + for (let i = 0; i < uniquePendingNotifications.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + expect(subscriptionsCollection.find.getCall(i).args[0]).to.deep.equal({ + type: SUBSCRIPTION_TYPE.entity, + entityId: pendingNotification.entity_id, + }); + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + + expect( + notificationsCollection.updateOne.getCalls().length, + 'Notifications marked as processed count' + ).to.be.equal(pendingNotifications.length); + }); + }); +}); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processIncidentUpdatesNotifications.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processIncidentUpdatesNotifications.cy.js new file mode 100644 index 0000000000..7a90a611c7 --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processIncidentUpdatesNotifications.cy.js @@ -0,0 +1,263 @@ +import { stubEverything } from './processNotificationsUtils'; + +const { SUBSCRIPTION_TYPE } = require('../../../../../src/utils/subscriptions'); + +const processNotifications = require('../../../../../../realm/functions/processNotifications'); + +const { recipients, reports, incidents } = require('./fixtures'); + +const pendingNotifications = [ + { + _id: '63616f82d0db19c07d081300', + type: 'incident-updated', + incident_id: 219, + processed: false, + }, + { + _id: '63616f82d0db19c07d081301', + type: 'new-report-incident', + incident_id: 219, + report_number: 2000, + processed: false, + }, + //Duplicated pending notification + { + _id: '63616f82d0db19c07d081302', + type: 'new-report-incident', + incident_id: 219, + report_number: 2000, + processed: false, + }, + { + _id: '63616f82d0db19c07d081303', + type: 'incident-updated', + incident_id: 219, + processed: false, + }, +]; + +const uniquePendingNotifications = pendingNotifications.slice(0, 2); + +const subscriptions = [ + { + userId: '63320ce63ec803072c9f5291', + type: SUBSCRIPTION_TYPE.incident, + incident_id: 219, + }, + { + userId: '63321072f27421740a80af22', + type: SUBSCRIPTION_TYPE.incident, + incident_id: 219, + }, +]; + +describe('Process Incident Updates Pending Notifications', () => { + it('Incident Updates - Should process all pending notifications', () => { + const { notificationsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.incident, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then((result) => { + expect( + notificationsCollection.updateOne.callCount, + 'Mark notification item as processed' + ).to.be.equal(pendingNotifications.length); + + const sendEmailCalls = global.context.functions.execute + .getCalls() + .filter((call) => call.args[0] === 'sendEmail'); + + expect(sendEmailCalls.length, 'sendEmail function calls').to.be.equal( + uniquePendingNotifications.length + ); + + // Check that the emails are sent only once + for (let i = 0; i < sendEmailCalls.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + const sendEmailCallArgs = sendEmailCalls[i].args[1]; + + const userIds = subscriptions + .filter((s) => s.incident_id === pendingNotification.incident_id) + .map((subscription) => subscription.userId); + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const newReportNumber = pendingNotification.report_number; + + const newReport = newReportNumber + ? reports.find((r) => r.report_number == pendingNotification.report_number) + : null; + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: 'Incident {{incidentId}} was updated', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, + reportUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}#r${newReportNumber}`, + reportTitle: newReportNumber ? newReport.title : '', + reportAuthor: newReportNumber && newReport.authors[0] ? newReport.authors[0] : '', + }, + templateId: newReportNumber // Template value from function name sufix from "site/realm/functions/config.json" + ? 'NewReportAddedToAnIncident' + : 'IncidentUpdate', + }; + + expect(sendEmailCallArgs, 'Send email args').to.be.deep.equal(sendEmailParams); + } + + //No Rollbar error logs + expect( + global.context.functions.execute.getCalls().filter((call) => call.args[0] === 'logRollbar') + .length, + 'logRollbar function calls' + ).to.be.equal(0); + + expect(result, 'Notifications processed count').to.be.equal(pendingNotifications.length); + }); + }); + + it('Incident Updates - Should send pending notifications', () => { + const { notificationsCollection, subscriptionsCollection, incidentsCollection } = + stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.incident, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then(() => { + expect( + notificationsCollection.find.getCall(2).args[0], + 'Get pending notifications for Incident Updates' + ).to.deep.equal({ + processed: false, + type: { $in: ['new-report-incident', 'incident-updated'] }, + }); + + for (let i = 0; i < uniquePendingNotifications.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + expect( + subscriptionsCollection.find.getCall(i).args[0], + 'Get subscriptions for Incident' + ).to.deep.equal({ + type: SUBSCRIPTION_TYPE.incident, + incident_id: pendingNotification.incident_id, + }); + + for (const subscription of subscriptions) { + expect(global.context.functions.execute).to.be.calledWith('getUser', { + userId: subscription.userId, + }); + } + + expect(incidentsCollection.findOne.getCall(i).args[0]).to.deep.equal({ + incident_id: pendingNotification.incident_id, + }); + + const userIds = subscriptions + .filter((s) => s.incident_id === pendingNotification.incident_id) + .map((subscription) => subscription.userId); + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const newReportNumber = pendingNotification.report_number; + + const newReport = newReportNumber + ? reports.find((r) => r.report_number == pendingNotification.report_number) + : null; + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: 'Incident {{incidentId}} was updated', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, + reportUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}#r${newReportNumber}`, + reportTitle: newReportNumber ? newReport.title : '', + reportAuthor: newReportNumber && newReport.authors[0] ? newReport.authors[0] : '', + }, + templateId: newReportNumber // Template value from function name sufix from "site/realm/functions/config.json" + ? 'NewReportAddedToAnIncident' + : 'IncidentUpdate', + }; + + expect(global.context.functions.execute, 'Send Email').to.be.calledWith( + 'sendEmail', + sendEmailParams + ); + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + }); + }); + + it('Incident Updates - Should mark pending notifications as processed if there are no subscribers', () => { + const { notificationsCollection, subscriptionsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.incident, + pendingNotifications, + subscriptions: [], + }); + + cy.wrap(processNotifications()).then(() => { + expect(notificationsCollection.find.getCall(0).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.newIncidents, + }); + + expect(notificationsCollection.find.getCall(1).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.entity, + }); + + expect(notificationsCollection.find.getCall(2).args[0]).to.deep.equal({ + processed: false, + type: { $in: ['new-report-incident', 'incident-updated'] }, + }); + + expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.submissionPromoted, + }); + + expect(global.context.functions.execute).not.to.be.called; + + for (let i = 0; i < uniquePendingNotifications.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + expect(subscriptionsCollection.find.getCall(i).args[0]).to.deep.equal({ + type: SUBSCRIPTION_TYPE.incident, + incident_id: pendingNotification.incident_id, + }); + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + + expect( + notificationsCollection.updateOne.getCalls().length, + 'Notifications marked as processed count' + ).to.be.equal(pendingNotifications.length); + }); + }); +}); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewIncidentsNotifications.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewIncidentsNotifications.cy.js new file mode 100644 index 0000000000..6bd6397aa0 --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewIncidentsNotifications.cy.js @@ -0,0 +1,235 @@ +import { buildEntityList, stubEverything } from './processNotificationsUtils'; + +const { SUBSCRIPTION_TYPE } = require('../../../../../src/utils/subscriptions'); + +const processNotifications = require('../../../../../../realm/functions/processNotifications'); + +const { recipients, entities, incidents } = require('./fixtures'); + +const pendingNotifications = [ + { + _id: '63616f37d0db19c07d081100', + type: SUBSCRIPTION_TYPE.newIncidents, + incident_id: 217, + processed: false, + }, + { + _id: '63616f82d0db19c07d081101', + type: SUBSCRIPTION_TYPE.newIncidents, + incident_id: 218, + processed: false, + }, + //Duplicated pending notification + { + _id: '63616f82d0db19c07d081102', + type: SUBSCRIPTION_TYPE.newIncidents, + incident_id: 218, + processed: false, + }, +]; + +const uniquePendingNotifications = pendingNotifications.slice(0, 2); + +const subscriptions = [ + { + _id: '6356e39e863169c997309586', + type: SUBSCRIPTION_TYPE.newIncidents, + userId: '63320ce63ec803072c9f5291', + }, + { + _id: '6356e39e863169c997309586', + type: SUBSCRIPTION_TYPE.newIncidents, + userId: '63321072f27421740a80af22', + }, +]; + +describe('Process New Incident Pending Notifications', () => { + it('New Incidents - Should process all pending notifications', () => { + const { notificationsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.newIncidents, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then((result) => { + expect( + notificationsCollection.updateOne.callCount, + 'Mark notification item as processed' + ).to.be.equal(pendingNotifications.length); + + const sendEmailCalls = global.context.functions.execute + .getCalls() + .filter((call) => call.args[0] === 'sendEmail'); + + expect(sendEmailCalls.length, 'sendEmail function calls').to.be.equal( + uniquePendingNotifications.length + ); + + // Check that the emails are sent only once + for (let i = 0; i < sendEmailCalls.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + const sendEmailCallArgs = sendEmailCalls[i].args[1]; + + const userIds = subscriptions.map((subscription) => subscription.userId); + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: 'New Incident {{incidentId}} was created', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${pendingNotification.incident_id}`, + incidentDescription: incident.description, + incidentDate: incident.date, + developers: buildEntityList(entities, incident['Alleged developer of AI system']), + deployers: buildEntityList(entities, incident['Alleged deployer of AI system']), + entitiesHarmed: buildEntityList( + entities, + incident['Alleged harmed or nearly harmed parties'] + ), + }, + templateId: 'NewIncident', // Template value from function name sufix from "site/realm/functions/config.json" + }; + + expect(sendEmailCallArgs, 'Send email args').to.be.deep.equal(sendEmailParams); + } + + //No Rollbar error logs + expect( + global.context.functions.execute.getCalls().filter((call) => call.args[0] === 'logRollbar') + .length, + 'logRollbar function calls' + ).to.be.equal(0); + + expect(result, 'Notifications processed count').to.be.equal(pendingNotifications.length); + }); + }); + + it('New Incidents - Should send pending notifications', () => { + const { notificationsCollection, subscriptionsCollection, incidentsCollection } = + stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.newIncidents, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then(() => { + expect(notificationsCollection.find.firstCall.args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.newIncidents, + }); + + expect(subscriptionsCollection.find.firstCall.args[0]).to.deep.equal({ + type: SUBSCRIPTION_TYPE.newIncidents, + }); + + for (const subscription of subscriptions) { + expect(global.context.functions.execute).to.be.calledWith('getUser', { + userId: subscription.userId, + }); + } + + for (let i = 0; i < uniquePendingNotifications.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + expect(incidentsCollection.findOne.getCall(i).args[0]).to.deep.equal({ + incident_id: pendingNotification.incident_id, + }); + + const userIds = subscriptions.map((subscription) => subscription.userId); + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: 'New Incident {{incidentId}} was created', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${pendingNotification.incident_id}`, + incidentDescription: incident.description, + incidentDate: incident.date, + developers: buildEntityList(entities, incident['Alleged developer of AI system']), + deployers: buildEntityList(entities, incident['Alleged deployer of AI system']), + entitiesHarmed: buildEntityList( + entities, + incident['Alleged harmed or nearly harmed parties'] + ), + }, + templateId: 'NewIncident', // Template value from function name sufix from "site/realm/functions/config.json" + }; + + expect(global.context.functions.execute).to.be.calledWith('sendEmail', sendEmailParams); + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + }); + }); + + it('New Incidents - Should mark pending notifications as processed if there are no subscribers', () => { + const { notificationsCollection, subscriptionsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.newIncidents, + pendingNotifications, + subscriptions: [], + }); + + cy.wrap(processNotifications()).then(() => { + expect(notificationsCollection.find.getCall(0).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.newIncidents, + }); + + expect(notificationsCollection.find.getCall(1).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.entity, + }); + + expect(notificationsCollection.find.getCall(2).args[0]).to.deep.equal({ + processed: false, + type: { $in: ['new-report-incident', 'incident-updated'] }, + }); + + expect(subscriptionsCollection.find.getCall(0).args[0]).to.deep.equal({ + type: SUBSCRIPTION_TYPE.newIncidents, + }); + + expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.submissionPromoted, + }); + + expect(global.context.functions.execute).not.to.be.called; + + for (let i = 0; i < pendingNotifications.length; i++) { + const pendingNotification = pendingNotifications[i]; + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + + expect( + notificationsCollection.updateOne.getCalls().length, + 'Notifications marked as processed count' + ).to.be.equal(pendingNotifications.length); + }); + }); +}); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewPromotionsNotifications.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewPromotionsNotifications.cy.js new file mode 100644 index 0000000000..68b8d6868e --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewPromotionsNotifications.cy.js @@ -0,0 +1,216 @@ +import { stubEverything } from './processNotificationsUtils'; + +const { SUBSCRIPTION_TYPE } = require('../../../../../src/utils/subscriptions'); + +const processNotifications = require('../../../../../../realm/functions/processNotifications'); + +const { recipients, incidents } = require('./fixtures'); + +const pendingNotifications = [ + { + _id: '63616f82d0db19c07d081400', + type: SUBSCRIPTION_TYPE.submissionPromoted, + incident_id: 217, + processed: false, + userId: '63320ce63ec803072c9f5291', + }, + { + _id: '63616f82d0db19c07d081401', + type: SUBSCRIPTION_TYPE.submissionPromoted, + incident_id: 218, + processed: false, + userId: '63320ce63ec803072c9f5291', + }, + //Duplicated pending notification + { + _id: '63616f82d0db19c07d081402', + type: SUBSCRIPTION_TYPE.submissionPromoted, + incident_id: 218, + processed: false, + userId: '63320ce63ec803072c9f5291', + }, +]; + +const uniquePendingNotifications = pendingNotifications.slice(0, 2); + +const subscriptions = [ + { + _id: '6356e39e863169c997309590', + type: SUBSCRIPTION_TYPE.submissionPromoted, + userId: '63320ce63ec803072c9f5291', + incident_id: 217, + }, + { + _id: '6356e39e863169c997309591', + type: SUBSCRIPTION_TYPE.submissionPromoted, + userId: '63321072f27421740a80af22', + incident_id: 218, + }, +]; + +describe('Process New Promotions Pending Notifications', () => { + it('New Promotions - Should process all pending notifications', () => { + const { notificationsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.submissionPromoted, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then((result) => { + expect( + notificationsCollection.updateOne.callCount, + 'Mark notification item as processed' + ).to.be.equal(pendingNotifications.length); + + const sendEmailCalls = global.context.functions.execute + .getCalls() + .filter((call) => call.args[0] === 'sendEmail'); + + expect(sendEmailCalls.length, 'sendEmail function calls').to.be.equal( + uniquePendingNotifications.length + ); + + // Check that the emails are sent only once + for (let i = 0; i < sendEmailCalls.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + const sendEmailCallArgs = sendEmailCalls[i].args[1]; + + const userIds = pendingNotifications.map((n) => n.userId); + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: 'Your submission has been approved!', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${pendingNotification.incident_id}`, + incidentDescription: incident.description, + incidentDate: incident.date, + }, + templateId: 'SubmissionApproved', // Template value from function name sufix from "site/realm/functions/config.json" + }; + + expect(sendEmailCallArgs, 'Send email args').to.be.deep.equal(sendEmailParams); + } + + //No Rollbar error logs + expect( + global.context.functions.execute.getCalls().filter((call) => call.args[0] === 'logRollbar') + .length, + 'logRollbar function calls' + ).to.be.equal(0); + + expect(result, 'Notifications processed count').to.be.equal(pendingNotifications.length); + }); + }); + + it('New Promotions - Should send pending submissions promoted notifications', () => { + const { notificationsCollection, incidentsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.submissionPromoted, + pendingNotifications, + subscriptions, + }); + + cy.wrap(processNotifications()).then(() => { + expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.submissionPromoted, + }); + + for (const notification of pendingNotifications) { + expect(global.context.functions.execute).to.be.calledWith('getUser', { + userId: notification.userId, + }); + } + + for (let i = 0; i < uniquePendingNotifications.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + expect(incidentsCollection.findOne.getCall(i).args[0], 'Find incident').to.deep.equal({ + incident_id: pendingNotification.incident_id, + }); + + const userIds = pendingNotifications.map((n) => n.userId); + + const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); + + const sendEmailParams = { + recipients: recipients.filter((r) => userIds.includes(r.userId)), + subject: 'Your submission has been approved!', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${pendingNotification.incident_id}`, + incidentDescription: incident.description, + incidentDate: incident.date, + }, + templateId: 'SubmissionApproved', // Template value from function name sufix from "site/realm/functions/config.json" + }; + + expect(global.context.functions.execute).to.be.calledWith('sendEmail', sendEmailParams); + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + }); + }); + + it('New Promotions - Should mark pending notifications as processed if there are no subscribers', () => { + const { notificationsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.submissionPromoted, + pendingNotifications, + subscriptions: [], + }); + + cy.wrap(processNotifications()).then(() => { + expect(notificationsCollection.find.getCall(0).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.newIncidents, + }); + + expect(notificationsCollection.find.getCall(1).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.entity, + }); + + expect(notificationsCollection.find.getCall(2).args[0]).to.deep.equal({ + processed: false, + type: { $in: ['new-report-incident', 'incident-updated'] }, + }); + + expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ + processed: false, + type: SUBSCRIPTION_TYPE.submissionPromoted, + }); + + for (let i = 0; i < uniquePendingNotifications.length; i++) { + const pendingNotification = uniquePendingNotifications[i]; + + expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ + _id: pendingNotification._id, + }); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set.processed).to.be.equal( + true + ); + expect(notificationsCollection.updateOne.getCall(i).args[1].$set).to.have.ownProperty( + 'sentDate' + ); + } + + expect( + notificationsCollection.updateOne.getCalls().length, + 'Notifications marked as processed count' + ).to.be.equal(pendingNotifications.length); + }); + }); +}); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNotificationsUtils.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNotificationsUtils.js new file mode 100644 index 0000000000..2c3d7df85d --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNotificationsUtils.js @@ -0,0 +1,239 @@ +const { SUBSCRIPTION_TYPE } = require('../../../../../src/utils/subscriptions'); + +const { recipients, entities, incidents, reports } = require('./fixtures'); + +export const buildEntityList = (allEntities, entityIds) => { + const entityNames = entityIds.map((entityId) => { + const entity = allEntities.find((entity) => entity.entity_id === entityId); + + return entity + ? `${entity.name}` + : ''; + }); + + if (entityNames.length < 3) { + return entityNames.join(' and '); + } + + return `${entityNames.slice(0, -1).join(', ')}, and ${entityNames[entityNames.length - 1]}`; +}; + +export const stubEverything = ({ subscriptionType, pendingNotifications, subscriptions }) => { + const notificationsCollection = { + find: (() => { + const stub = cy.stub(); + + // Initiate empty stubs for all types + stub + .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.newIncidents }) + .as(`notifications.find(${SUBSCRIPTION_TYPE.newIncidents})`) + .returns({ toArray: () => [] }); + + stub + .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.entity }) + .as(`notifications.find(${SUBSCRIPTION_TYPE.entity})`) + .returns({ toArray: () => [] }); + + stub + .withArgs({ processed: false, type: { $in: ['new-report-incident', 'incident-updated'] } }) + .as(`notifications.find('new-report-incident', 'incident-updated')`) + .returns({ toArray: () => [] }); + + stub + .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.submissionPromoted }) + .as(`notifications.find(${SUBSCRIPTION_TYPE.submissionPromoted})`) + .returns({ toArray: () => [] }); + + // Override stubs for specific types + switch (subscriptionType) { + case SUBSCRIPTION_TYPE.newIncidents: + stub + .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.newIncidents }) + .as(`notifications.find(${SUBSCRIPTION_TYPE.newIncidents})`) + .returns({ toArray: () => pendingNotifications }); + break; + + case SUBSCRIPTION_TYPE.entity: + stub + .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.entity }) + .as(`notifications.find(${SUBSCRIPTION_TYPE.entity})`) + .returns({ toArray: () => pendingNotifications }); + break; + + case SUBSCRIPTION_TYPE.incident: + stub + .withArgs({ + processed: false, + type: { $in: ['new-report-incident', 'incident-updated'] }, + }) + .as(`notifications.find('new-report-incident', 'incident-updated')`) + .returns({ toArray: () => pendingNotifications }); + break; + + case SUBSCRIPTION_TYPE.submissionPromoted: + stub + .withArgs({ processed: false, type: SUBSCRIPTION_TYPE.submissionPromoted }) + .as(`notifications.find(${SUBSCRIPTION_TYPE.submissionPromoted})`) + .returns({ toArray: () => pendingNotifications }); + break; + } + + return stub; + })(), + updateOne: cy.stub().as('notifications.updateOne').resolves(), + }; + + const subscriptionsCollection = { + find: (() => { + const stub = cy.stub(); + + switch (subscriptionType) { + case SUBSCRIPTION_TYPE.newIncidents: + stub + .withArgs({ type: SUBSCRIPTION_TYPE.newIncidents }) + .as(`subscriptions.find("${SUBSCRIPTION_TYPE.newIncidents}")`) + .returns({ toArray: () => subscriptions }); + break; + + case SUBSCRIPTION_TYPE.entity: + for (const pendingNotification of pendingNotifications) { + stub + .withArgs({ type: SUBSCRIPTION_TYPE.entity, entityId: pendingNotification.entity_id }) + .as( + `subscriptions.find("${SUBSCRIPTION_TYPE.entity}", "${pendingNotification.entity_id}")` + ) + .returns({ + toArray: () => + subscriptions.filter((s) => s.entityId === pendingNotification.entity_id), + }); + } + break; + + case SUBSCRIPTION_TYPE.incident: + for (const pendingNotification of pendingNotifications) { + stub + .withArgs({ + type: SUBSCRIPTION_TYPE.incident, + incident_id: pendingNotification.incident_id, + }) + .as( + `subscriptions.find("${SUBSCRIPTION_TYPE.incident}", "${pendingNotification.incident_id}")` + ) + .returns({ + toArray: () => + subscriptions.filter((s) => s.incident_id === pendingNotification.incident_id), + }); + } + break; + + case SUBSCRIPTION_TYPE.submissionPromoted: + for (const pendingNotification of pendingNotifications) { + stub + .withArgs({ + type: SUBSCRIPTION_TYPE.submissionPromoted, + incident_id: pendingNotification.incident_id, + }) + .as( + `subscriptions.find("${SUBSCRIPTION_TYPE.submissionPromoted}", "${pendingNotification.incident_id}")` + ) + .returns({ + toArray: () => + subscriptions.filter((s) => s.incident_id === pendingNotification.incident_id), + }); + } + break; + } + + return stub; + })(), + }; + + const incidentsCollection = { + findOne: (() => { + const stub = cy.stub(); + + for (let index = 0; index < incidents.length; index++) { + const incident = incidents[index]; + + stub + .withArgs({ incident_id: incident.incident_id }) + .as(`incidents.findOne(${incident.incident_id})`) + .returns(incidents.find((i) => i.incident_id == incident.incident_id)); + } + + return stub; + })(), + }; + + const reportsCollection = { + findOne: (() => { + const stub = cy.stub(); + + for (let index = 0; index < reports.length; index++) { + const report = reports[index]; + + stub + .withArgs({ report_number: report.report_number }) + .as(`reports.findOne(${report.report_number})`) + .returns(reports.find((r) => r.report_number == report.report_number)); + } + + return stub; + })(), + }; + + const entitiesCollection = { + find: cy.stub().returns({ + toArray: cy.stub().as('entities.find').resolves(entities), + }), + }; + + global.context = { + // @ts-ignore + services: { + get: cy.stub().returns({ + db: cy.stub().returns({ + collection: (() => { + const stub = cy.stub(); + + stub.withArgs('notifications').returns(notificationsCollection); + stub.withArgs('subscriptions').returns(subscriptionsCollection); + stub.withArgs('incidents').returns(incidentsCollection); + stub.withArgs('entities').returns(entitiesCollection); + stub.withArgs('reports').returns(reportsCollection); + + return stub; + })(), + }), + }), + }, + functions: { + execute: (() => { + const stub = cy.stub(); + + for (const user of recipients) { + stub + .withArgs('getUser', { userId: user.userId }) + .as(`getUser(${user.userId})`) + .returns(recipients.find((r) => r.userId == user.userId)); + } + + stub.withArgs('sendEmail').as('sendEmail').returns({ statusCode: 200 }); + + stub.withArgs('logRollbar').as('logRollbar').returns({ statusCode: 200 }); + + return stub; + })(), + }, + }; + + global.BSON = { Int32: (x) => x }; + + return { + notificationsCollection, + subscriptionsCollection, + incidentsCollection, + entitiesCollection, + reportsCollection, + }; +}; diff --git a/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js index bc100dea5b..6bc67a8a00 100644 --- a/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js +++ b/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js @@ -225,10 +225,10 @@ describe('Functions', () => { report_number: 2, is_incident_report: true, title: 'Submisssion 1 title', - date_downloaded: '2020-10-30', - date_modified: '2021-07-27', - date_published: '2017-05-03', - date_submitted: '2020-10-30', + date_downloaded: new Date('2020-10-30'), + date_modified: new Date('2021-07-27'), + date_published: new Date('2017-05-03'), + date_submitted: new Date('2020-10-30'), epoch_date_downloaded: 1604016000, epoch_date_modified: 1686182943, epoch_date_published: 1493769600, @@ -260,10 +260,11 @@ describe('Functions', () => { modifiedBy: submission.user, }); - expect(subscriptionsCollection.insertOne.firstCall.args[0]).to.deep.equal({ + expect(notificationsCollection.insertOne.firstCall.args[0]).to.deep.equal({ type: SUBSCRIPTION_TYPE.submissionPromoted, incident_id: 2, userId: 'user1', + processed: false, }); }); }); @@ -384,10 +385,10 @@ describe('Functions', () => { report_number: 2, is_incident_report: true, title: 'Submisssion 1 title', - date_downloaded: '2020-10-30', - date_modified: '2021-07-27', - date_published: '2017-05-03', - date_submitted: '2020-10-30', + date_downloaded: new Date('2020-10-30'), + date_modified: new Date('2021-07-27'), + date_published: new Date('2017-05-03'), + date_submitted: new Date('2020-10-30'), epoch_date_downloaded: 1604016000, epoch_date_modified: 1686182943, epoch_date_published: 1493769600, @@ -541,10 +542,10 @@ describe('Functions', () => { report_number: 2, is_incident_report: false, title: 'Submisssion 1 title', - date_downloaded: '2020-10-30', - date_modified: '2021-07-27', - date_published: '2017-05-03', - date_submitted: '2020-10-30', + date_downloaded: new Date('2020-10-30'), + date_modified: new Date('2021-07-27'), + date_published: new Date('2017-05-03'), + date_submitted: new Date('2020-10-30'), epoch_date_downloaded: 1604016000, epoch_date_modified: 1686182943, epoch_date_published: 1493769600, @@ -843,10 +844,10 @@ describe('Functions', () => { report_number: 2, is_incident_report: true, title: 'Submisssion 1 title', - date_downloaded: '2020-10-30', - date_modified: '2021-07-27', - date_published: '2017-05-03', - date_submitted: '2020-10-30', + date_downloaded: new Date('2020-10-30'), + date_modified: new Date('2021-07-27'), + date_published: new Date('2017-05-03'), + date_submitted: new Date('2020-10-30'), epoch_date_downloaded: 1604016000, epoch_date_modified: 1686182943, epoch_date_published: 1493769600, diff --git a/site/gatsby-site/cypress/e2e/unit/migrations/2023.11.29T14.56.25.delete-duplicated-subscriptions.cy.js b/site/gatsby-site/cypress/e2e/unit/migrations/2023.11.29T14.56.25.delete-duplicated-subscriptions.cy.js new file mode 100644 index 0000000000..956861ad3d --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/migrations/2023.11.29T14.56.25.delete-duplicated-subscriptions.cy.js @@ -0,0 +1,149 @@ +const { ObjectID } = require('bson'); + +const { + up, +} = require('../../../../migrations/2023.11.29T14.56.25.delete-duplicated-subscriptions'); + +describe('Migration Script - Remove Duplicated Subscriptions', () => { + it('Should remove duplicated subscriptions correctly', () => { + // Mocked data for all three cases + const testSubscriptions = { + incident: [ + { + _id: { + type: 'incident', + userId: '642188372947d07020c1319d', + incident_id: 600, + }, + uniqueIds: [ + new ObjectID('5f9f6b9b5f9c4c0001a3b3a3'), + new ObjectID('5f9f6b9b5f9c4c0001a3b3a4'), + ], + }, + { + _id: { + type: 'submission-promoted', + userId: '642188372947d07020c1319d', + incident_id: 600, + }, + uniqueIds: [ + new ObjectID('5f9f6b9b5f9c4c0001a3b3a0'), + new ObjectID('5f9f6b9b5f9c4c0001a3b3a1'), + new ObjectID('5f9f6b9b5f9c4c0001a3b3a2'), + ], + }, + ], + entity: [ + { + _id: { + type: 'entity', + userId: '642188372947d07020c1319d', + entity_id: 'trans-women', + }, + uniqueIds: [ + new ObjectID('5f9f6b9b5f9c4c0001a3b3a5'), + new ObjectID('5f9f6b9b5f9c4c0001a3b3a6'), + ], + }, + ], + 'new-incidents': [ + { + _id: { + type: 'new-incidents', + userId: '642188372947d07020c1319d', + }, + uniqueIds: [ + new ObjectID('5f9f6b9b5f9c4c0001a3b3a7'), + new ObjectID('5f9f6b9b5f9c4c0001a3b3a8'), + ], + }, + ], + }; + + // Mocked collection with stubbed methods + const subscriptionsCollection = { + aggregate: cy.stub().callsFake((query) => { + const type = query[0].$match.type.$in[0]; + + return { + toArray: cy.stub().resolves(testSubscriptions[type]), + }; + }), + deleteMany: cy.stub().callsFake((query) => { + console.log('deleteMany called with:', query._id.$in[0].toString()); // Log the query + return Promise.resolve({ deletedCount: 1 }); + }), + }; + + // Mocked context with database client + const context = { + client: { + db: cy.stub().returns({ + collection: cy.stub().withArgs('subscriptions').returns(subscriptionsCollection), + }), + }, + }; + + // Execute the migration script + cy.wrap(up({ context })).then(() => { + // Assertions for each case + const args = subscriptionsCollection.deleteMany.getCall(0).args[0]; + + const argsSubmissionPromoted = subscriptionsCollection.deleteMany.getCall(1).args[0]; + + const argsEntity = subscriptionsCollection.deleteMany.getCall(2).args[0]; + + const argsNewIncidents = subscriptionsCollection.deleteMany.getCall(3).args[0]; + + let modifiedObjectIncident = { + _id: { + $in: args._id.$in.map((id) => id.toString()), + }, + }; + + let modifiedObjectSubmissionPromoted = { + _id: { + $in: argsSubmissionPromoted._id.$in.map((id) => id.toString()), + }, + }; + + let modifiedObjectEntity = { + _id: { + $in: argsEntity._id.$in.map((id) => id.toString()), + }, + }; + + let modifiedObjectNewIncidents = { + _id: { + $in: argsNewIncidents._id.$in.map((id) => id.toString()), + }, + }; + + expect(modifiedObjectIncident).to.be.deep.equal({ + _id: { + $in: testSubscriptions['incident'][0].uniqueIds.slice(0, 1).map((id) => id.toString()), + }, + }); + + expect(modifiedObjectSubmissionPromoted).to.be.deep.equal({ + _id: { + $in: testSubscriptions['incident'][1].uniqueIds.slice(0, 2).map((id) => id.toString()), + }, + }); + + expect(modifiedObjectEntity).to.be.deep.equal({ + _id: { + $in: testSubscriptions['entity'][0].uniqueIds.slice(0, 1).map((id) => id.toString()), + }, + }); + + expect(modifiedObjectNewIncidents).to.be.deep.equal({ + _id: { + $in: testSubscriptions['new-incidents'][0].uniqueIds + .slice(0, 1) + .map((id) => id.toString()), + }, + }); + }); + }); +}); diff --git a/site/gatsby-site/cypress/support/e2e.js b/site/gatsby-site/cypress/support/e2e.js index 99370aea27..0b15c846ce 100644 --- a/site/gatsby-site/cypress/support/e2e.js +++ b/site/gatsby-site/cypress/support/e2e.js @@ -13,6 +13,8 @@ // https://on.cypress.io/configuration // *********************************************************** +import '@cypress/code-coverage/support'; + // Import commands.js using ES2015 syntax: import './commands'; diff --git a/site/gatsby-site/cypress/support/utils.js b/site/gatsby-site/cypress/support/utils.js index afb5effc18..2945a4aaba 100644 --- a/site/gatsby-site/cypress/support/utils.js +++ b/site/gatsby-site/cypress/support/utils.js @@ -4,13 +4,13 @@ import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'; export const maybeIt = Cypress.env('e2eUsername') && Cypress.env('e2ePassword') ? it : it.skip; -export const conditionalIt = (condition, name, fn) => { - if (condition) { - it(name, fn); - } else { - it.skip(name, fn); - } -}; +export const conditionalIt = (condition, ...rest) => + condition ? it.call(null, ...rest) : it.skip.call(null, ...rest); + +conditionalIt.only = (condition, ...rest) => + condition ? it.only.call(null, ...rest) : it.skip.call(null, ...rest); + +conditionalIt.skip = (condition, ...rest) => it.skip.call(null, ...rest); export const getApolloClient = () => { const email = Cypress.env('e2eUsername'); diff --git a/site/gatsby-site/gatsby-config.js b/site/gatsby-site/gatsby-config.js index 98e9eddd8a..3e0879042e 100755 --- a/site/gatsby-site/gatsby-config.js +++ b/site/gatsby-site/gatsby-config.js @@ -220,20 +220,6 @@ const plugins = [ ], }, }, - { - resolve: `gatsby-source-s3`, - options: { - aws: { - // This AWS IAM user has been provisioned no permissions, but the plugin requires a user to - // get a listing of the public S3 bucket. User: backupindexpublic - accessKeyId: 'AKIA25BP4AERUFDGAJUJ', - secretAccessKeyId: 'backupindexpublic', - secretAccessKey: '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', - }, - buckets: ['aiid-backups-public'], - // expiration: 120, - }, - }, { resolve: `gatsby-theme-i18n`, options: { @@ -263,6 +249,7 @@ const plugins = [ 'variants', 'footer', 'sponsors', + 'incidents', ], debug: process.env.GATSBY_I18N_DEBUG, nsSeparator: false, diff --git a/site/gatsby-site/gatsby-node.js b/site/gatsby-site/gatsby-node.js index 7749880941..198df63547 100644 --- a/site/gatsby-site/gatsby-node.js +++ b/site/gatsby-site/gatsby-node.js @@ -114,6 +114,15 @@ exports.onCreateBabelConfig = ({ actions }) => { actions.setBabelPlugin({ name: '@babel/plugin-proposal-export-default-from', }); + + if (process.env.INSTRUMENT) { + actions.setBabelPlugin({ + name: 'babel-plugin-istanbul', + options: { + include: ['src/**/*.js'], + }, + }); + } }; exports.onCreateNode = async ({ node, getNode, actions }) => { diff --git a/site/gatsby-site/github-netlify.toml b/site/gatsby-site/github-netlify.toml new file mode 100644 index 0000000000..d5eb03f655 --- /dev/null +++ b/site/gatsby-site/github-netlify.toml @@ -0,0 +1,14 @@ +[[plugins]] + package = "@netlify/plugin-gatsby" + +[[plugins]] + package = "/plugins/netlify-plugin-create-admin-user" + +# [[plugins]] +# package = "/plugins/netlify-plugin-process-notifications" + +[build.processing.html] + pretty_urls = false + +[context.deploy-preview] + publish = "public/" \ No newline at end of file diff --git a/site/gatsby-site/i18n/locales/en/incidents.json b/site/gatsby-site/i18n/locales/en/incidents.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/site/gatsby-site/i18n/locales/en/incidents.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/site/gatsby-site/i18n/locales/es/incidents.json b/site/gatsby-site/i18n/locales/es/incidents.json new file mode 100644 index 0000000000..bc378f1f03 --- /dev/null +++ b/site/gatsby-site/i18n/locales/es/incidents.json @@ -0,0 +1,7 @@ +{ + "Incident List": "Lista de Incidentes", + "This is a simple numeric listing of all incidents and their reports within the database. If you would like to explore the contents of the reports, you should work through the<1> Discover app.": "Se trata de una lista numérica simple de todos los incidentes y sus reportes dentro de la base de datos. Si desea explorar el contenido de los reportes, debe navegar a través de<1> Descubrir Incidentes.", + "Sort by incident ID": "Ordenar por ID de incidente", + "ascending": "ascendente", + "descending": "descendente" +} \ No newline at end of file diff --git a/site/gatsby-site/i18n/locales/es/translation.json b/site/gatsby-site/i18n/locales/es/translation.json index a2a6683179..f1e7baab4d 100644 --- a/site/gatsby-site/i18n/locales/es/translation.json +++ b/site/gatsby-site/i18n/locales/es/translation.json @@ -283,6 +283,7 @@ "csetChartDeveloped": "CSET ha desarrollado definiciones específicas para las frases subrayadas que pueden diferir de las definiciones de otras organizaciones. Como resultado, otras organizaciones pueden hacer diferentes evaluaciones sobre si un incidente de IA en particular es (o no) un daño de IA. Los detalles sobre las definiciones de CSET para el daño de la IA se pueden encontrar <1>aquí.", "csetChartMail": "Cada incidente es clasificado de forma independiente por dos anotadores CSET. Las anotaciones se revisan por pares y finalmente se seleccionan al azar para el control de calidad antes de la publicación. A pesar de este riguroso proceso, ocurren errores y se invita a los lectores a <1>informar de cualquier error que puedan descubrir mientras navegan.", "[Untitled Report]": "[Informe sin título]", + "YYYY-MM-DD": "AAAA-MM-DD", "Incidents": "Incidentes", "Incident and Issue Reports": "Incidentes e Informes de Problemas", "Issue Reports": "Informes de Problemas", diff --git a/site/gatsby-site/i18n/locales/fr/incidents.json b/site/gatsby-site/i18n/locales/fr/incidents.json new file mode 100644 index 0000000000..e9a40cca4c --- /dev/null +++ b/site/gatsby-site/i18n/locales/fr/incidents.json @@ -0,0 +1,7 @@ +{ + "Incident List": "Liste des Incidents", + "This is a simple numeric listing of all incidents and their reports within the database. If you would like to explore the contents of the reports, you should work through the<1> Discover app.": "Il s'agit d'une simple liste numérique de tous les incidents et de leurs rapports dans la base de données. Si vous souhaitez explorer le contenu des rapports, vous devez parcourir <1> Découvrir les incidents.", + "Sort by incident ID": "Trier par ID d'incident", + "ascending": "ascendant", + "descending": "descendant" +} \ No newline at end of file diff --git a/site/gatsby-site/i18n/locales/fr/translation.json b/site/gatsby-site/i18n/locales/fr/translation.json index 8f7b0208ba..98d3bedb7b 100644 --- a/site/gatsby-site/i18n/locales/fr/translation.json +++ b/site/gatsby-site/i18n/locales/fr/translation.json @@ -271,6 +271,7 @@ "csetChartDeveloped": "Le CSET a développé des définitions spécifiques pour les phrases soulignées qui peuvent différer des définitions d'autres organisations. Par conséquent, d'autres organisations peuvent procéder à des évaluations différentes pour déterminer si un incident d'IA particulier est (ou n'est pas) un préjudice lié à l'IA. Des détails sur les définitions du CSET pour les dommages causés par l'IA peuvent être trouvés <1>ici.", "csetChartMail": "Chaque incident est classé indépendamment par deux annotateurs CSET. Les annotations sont examinées par des pairs et finalement sélectionnées au hasard pour un contrôle qualité avant publication. Malgré ce processus rigoureux, des erreurs se produisent et les lecteurs sont invités à <1>signaler toute erreur qu'ils pourraient découvrir en naviguant.", "[Untitled Report]": "[Rapport sans titre]", + "YYYY-MM-DD": "AAAA-MM-JJ", "Incidents": "Incidents", "Incident and Issue Reports": "Incidents et rapports de problèmes", "Issue Reports": "Rapports de problèmes", diff --git a/site/gatsby-site/migrations/2023.11.13T13.39.12.update-reports-date-types.js b/site/gatsby-site/migrations/2023.11.13T13.39.12.update-reports-date-types.js new file mode 100644 index 0000000000..ec373e816b --- /dev/null +++ b/site/gatsby-site/migrations/2023.11.13T13.39.12.update-reports-date-types.js @@ -0,0 +1,79 @@ +const config = require('../config'); + +/** + * + * @param {{context: {client: import('mongodb').MongoClient}}} context + */ + +exports.up = async ({ context: { client } }) => { + await client.connect(); + + const db = client.db(config.realm.production_db.db_name); + + const dbHistory = client.db(config.realm.production_db.db_name_history); + + const reports = db.collection('reports'); + + const reportsHistory = dbHistory.collection('reports'); + + let docs = await reports.find({}).toArray(); + + let docsHistory = await reportsHistory.find({}).toArray(); + + await Promise.all( + docs.map((doc) => { + // Convert epoch timestamps (in seconds) to milliseconds, and then to MongoDB's Date type + const date_downloaded = new Date(doc.epoch_date_downloaded * 1000); + + const date_modified = new Date(doc.epoch_date_modified * 1000); + + const date_published = new Date(doc.epoch_date_published * 1000); + + const date_submitted = new Date(doc.epoch_date_submitted * 1000); + + // Update the collection with the new Date type values + return reports.updateOne( + { _id: doc._id }, + { + $set: { + date_downloaded: date_downloaded, + date_modified: date_modified, + date_published: date_published, + date_submitted: date_submitted, + }, + } + ); + }) + ); + + await Promise.all( + docsHistory.map((doc) => { + // Convert epoch timestamps (in seconds) to milliseconds, and then to MongoDB's Date type + const date_downloaded = new Date(doc.epoch_date_downloaded * 1000); + + const date_modified = new Date(doc.epoch_date_modified * 1000); + + const date_published = new Date(doc.epoch_date_published * 1000); + + const date_submitted = new Date(doc.epoch_date_submitted * 1000); + + // Update the collection with the new Date type values + return reports.updateOne( + { _id: doc._id }, + { + $set: { + date_downloaded: date_downloaded, + date_modified: date_modified, + date_published: date_published, + date_submitted: date_submitted, + }, + } + ); + }) + ); + + console.log('Migration completed!'); +}; + +/** @type {import('umzug').MigrationFn} */ +exports.down = async () => {}; diff --git a/site/gatsby-site/migrations/2023.11.29T14.56.25.delete-duplicated-subscriptions.js b/site/gatsby-site/migrations/2023.11.29T14.56.25.delete-duplicated-subscriptions.js new file mode 100644 index 0000000000..51b44f3d86 --- /dev/null +++ b/site/gatsby-site/migrations/2023.11.29T14.56.25.delete-duplicated-subscriptions.js @@ -0,0 +1,113 @@ +const { ObjectID } = require('bson'); + +const config = require('../config'); + +/** @type {import('umzug').MigrationFn} */ +exports.up = async ({ context: { client } }) => { + const db = client.db(config.realm.production_db.db_custom_data); + + const subscriptions = db.collection('subscriptions'); + + // Remove duplicates of type "incident" and "submission-promoted", based on type, incident_id and userId + + const result = await subscriptions + .aggregate([ + { + $match: { + type: { $in: ['incident', 'submission-promoted'] }, + }, + }, + { + $group: { + _id: { incident_id: '$incident_id', type: '$type', userId: '$userId' }, + uniqueIds: { $addToSet: '$_id' }, + count: { $sum: 1 }, + }, + }, + { + $match: { + count: { $gt: 1 }, + }, + }, + ]) + .toArray(); + + await removeDuplicates(subscriptions, result); + + // Remove duplicates of type "entity", based on type, entityId and userId + const resultEntities = await subscriptions + .aggregate([ + { + $match: { + type: { $in: ['entity'] }, + }, + }, + { + $group: { + _id: { entityId: '$entityId', type: '$type', userId: '$userId' }, + uniqueIds: { $addToSet: '$_id' }, + count: { $sum: 1 }, + }, + }, + { + $match: { + count: { $gt: 1 }, + }, + }, + ]) + .toArray(); + + await removeDuplicates(subscriptions, resultEntities); + + // Remove duplicates of type "new-incidents" based on type, incident_id and userId + + const resultNewIncidents = await subscriptions + .aggregate([ + { + $match: { + type: { $in: ['new-incidents'] }, + }, + }, + { + $group: { + _id: { incident_id: '$incident_id', type: '$type', userId: '$userId' }, + uniqueIds: { $addToSet: '$_id' }, + count: { $sum: 1 }, + }, + }, + { + $match: { + count: { $gt: 1 }, + }, + }, + ]) + .toArray(); + + await removeDuplicates(subscriptions, resultNewIncidents); +}; + +async function removeDuplicates(subscriptions, results) { + for (const doc of results) { + const uniqueIds = doc.uniqueIds.map((id) => new ObjectID(id)); + + if (doc._id.type && doc._id.userId) { + console.log( + `Removing ${doc.count - 1} duplicated subscriptions of type ${doc._id.type} for user ${ + doc._id.userId + } ${ + doc._id.incident_id + ? ' and for incident ' + doc._id.incident_id + : doc._id.entityId + ? ' and for entity ' + doc._id.entityId + : '' + }` + ); + uniqueIds.pop(); // Remove one from the array to keep + const deleteResult = await subscriptions.deleteMany({ + _id: { $in: uniqueIds }, + }); + + console.log('Delete Result: ', deleteResult); + } + } +} diff --git a/site/gatsby-site/migrations/2023.12.07T20.45.09.mark-pending-notifications-as-processed.js b/site/gatsby-site/migrations/2023.12.07T20.45.09.mark-pending-notifications-as-processed.js new file mode 100644 index 0000000000..59a6e9b0b9 --- /dev/null +++ b/site/gatsby-site/migrations/2023.12.07T20.45.09.mark-pending-notifications-as-processed.js @@ -0,0 +1,16 @@ +const config = require('../config'); + +/** @type {import('umzug').MigrationFn} */ +exports.up = async ({ context: { client } }) => { + const db = client.db(config.realm.production_db.db_custom_data); + + const notifications = db.collection('notifications'); + + // Mark all pending notifications as processed + const result = await notifications.updateMany( + { processed: false }, + { $set: { processed: true } } + ); + + console.log(`All pending notifications marked as processed. Total: ${result.modifiedCount}`); +}; diff --git a/site/gatsby-site/netlify.toml b/site/gatsby-site/netlify.toml index 8ea63c1b5c..65d69fe810 100644 --- a/site/gatsby-site/netlify.toml +++ b/site/gatsby-site/netlify.toml @@ -19,9 +19,8 @@ TERM = "xterm" start = 'gatsby serve -p 8080' browser = 'chromium' -# tmp disable -# [[plugins]] -# package = "/plugins/netlify-plugin-process-notifications" +[[plugins]] + package = "/plugins/netlify-plugin-process-notifications" [build.processing.html] - pretty_urls = false \ No newline at end of file + pretty_urls = false diff --git a/site/gatsby-site/package-lock.json b/site/gatsby-site/package-lock.json index 7d8caa630a..770ab0e4b9 100644 --- a/site/gatsby-site/package-lock.json +++ b/site/gatsby-site/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@apollo/client": "^3.7.8", - "@babel/plugin-proposal-export-default-from": "^7.12.13", + "@aws-sdk/client-s3": "^3.435.0", "@billboard.js/react": "^1.0.1", "@bytemd/react": "^1.15.0", "@cloudinary/base": "^1.0.0-beta.4", @@ -58,7 +58,6 @@ "gatsby-source-filesystem": "^5.0.0", "gatsby-source-mongodb": "^5.0.0", "gatsby-source-prismic": "^5.3.1", - "gatsby-source-s3": "^3.2.3", "gatsby-transformer-sharp": "^5.7.0", "graphql": "^16.8.1", "graphql-http": "^1.18.0", @@ -74,6 +73,7 @@ "react-bootstrap-daterangepicker": "^7.0.0", "react-bootstrap-typeahead": "6.0.0-alpha.6", "react-d3-cloud": "^1.0.6", + "react-datetime": "^3.2.0", "react-dom": "^18.2.0", "react-feather": "^2.0.9", "react-helmet": "^6.1.0", @@ -92,7 +92,7 @@ "remark-gfm": "^3.0.1", "rich-text-diff": "^0.2.3", "sass": "^1.54.5", - "sharp": "^0.32.4", + "sharp": "^0.32.6", "slugify": "^1.6.5", "stemmer": "^1.0.5", "stopword": "^1.0.7", @@ -105,10 +105,13 @@ "yup": "^0.29.3" }, "devDependencies": { + "@babel/plugin-proposal-export-default-from": "^7.23.3", + "@cypress/code-coverage": "^3.12.15", "@netlify/plugin-gatsby": "^3.7.0", "@tailwindcss/typography": "^0.5.8", "autoprefixer": "^10.4.7", "babel-eslint": "^10.1.0", + "babel-plugin-istanbul": "^6.1.1", "cypress": "^13.0.0", "cypress-wait-for-stable-dom": "^0.1.0", "eslint": "^7.17.0", @@ -426,7 +429,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "optional": true, "dependencies": { "@aws-crypto/util": "^3.0.0", "@aws-sdk/types": "^3.222.0", @@ -436,14 +438,27 @@ "node_modules/@aws-crypto/crc32/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/crc32c": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", + "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32c/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/ie11-detection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "optional": true, "dependencies": { "tslib": "^1.11.1" } @@ -451,14 +466,31 @@ "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", + "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/sha256-browser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "optional": true, "dependencies": { "@aws-crypto/ie11-detection": "^3.0.0", "@aws-crypto/sha256-js": "^3.0.0", @@ -473,14 +505,12 @@ "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/sha256-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "optional": true, "dependencies": { "@aws-crypto/util": "^3.0.0", "@aws-sdk/types": "^3.222.0", @@ -490,14 +520,12 @@ "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "optional": true, "dependencies": { "tslib": "^1.11.1" } @@ -505,14 +533,12 @@ "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/util": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "optional": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", @@ -522,8 +548,7 @@ "node_modules/@aws-crypto/util/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-sdk/client-cognito-identity": { "version": "3.370.0", @@ -572,6 +597,942 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.435.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.435.0.tgz", + "integrity": "sha512-jyuv0SLLwc7Wa0s0eWHs1G4V0EJB2+4Nl/yn/LhEUrcDPrCI2FHd/lLudSmrEW+s7Rty0KTx5ZzeTn6YZ6ohTQ==", + "dependencies": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.435.0", + "@aws-sdk/credential-provider-node": "3.435.0", + "@aws-sdk/middleware-bucket-endpoint": "3.433.0", + "@aws-sdk/middleware-expect-continue": "3.433.0", + "@aws-sdk/middleware-flexible-checksums": "3.433.0", + "@aws-sdk/middleware-host-header": "3.433.0", + "@aws-sdk/middleware-location-constraint": "3.433.0", + "@aws-sdk/middleware-logger": "3.433.0", + "@aws-sdk/middleware-recursion-detection": "3.433.0", + "@aws-sdk/middleware-sdk-s3": "3.433.0", + "@aws-sdk/middleware-signing": "3.433.0", + "@aws-sdk/middleware-ssec": "3.433.0", + "@aws-sdk/middleware-user-agent": "3.433.0", + "@aws-sdk/region-config-resolver": "3.433.0", + "@aws-sdk/signature-v4-multi-region": "3.433.0", + "@aws-sdk/types": "3.433.0", + "@aws-sdk/util-endpoints": "3.433.0", + "@aws-sdk/util-user-agent-browser": "3.433.0", + "@aws-sdk/util-user-agent-node": "3.433.0", + "@aws-sdk/xml-builder": "3.310.0", + "@smithy/config-resolver": "^2.0.16", + "@smithy/eventstream-serde-browser": "^2.0.12", + "@smithy/eventstream-serde-config-resolver": "^2.0.12", + "@smithy/eventstream-serde-node": "^2.0.12", + "@smithy/fetch-http-handler": "^2.2.4", + "@smithy/hash-blob-browser": "^2.0.12", + "@smithy/hash-node": "^2.0.12", + "@smithy/hash-stream-node": "^2.0.12", + "@smithy/invalid-dependency": "^2.0.12", + "@smithy/md5-js": "^2.0.12", + "@smithy/middleware-content-length": "^2.0.14", + "@smithy/middleware-endpoint": "^2.1.3", + "@smithy/middleware-retry": "^2.0.18", + "@smithy/middleware-serde": "^2.0.12", + "@smithy/middleware-stack": "^2.0.6", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/node-http-handler": "^2.1.8", + "@smithy/protocol-http": "^3.0.8", + "@smithy/smithy-client": "^2.1.12", + "@smithy/types": "^2.4.0", + "@smithy/url-parser": "^2.0.12", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.16", + "@smithy/util-defaults-mode-node": "^2.0.21", + "@smithy/util-retry": "^2.0.5", + "@smithy/util-stream": "^2.0.17", + "@smithy/util-utf8": "^2.0.0", + "@smithy/util-waiter": "^2.0.12", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "version": "3.435.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.435.0.tgz", + "integrity": "sha512-tT2bpwFZ3RStgyaS+JzFF4Yj+l4JRXP5+4ZRrIX5DFimzCUT8koeP4t2Gb6lvVD3DJL0nwGU5MODI1YbHTqZSQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.433.0", + "@aws-sdk/middleware-logger": "3.433.0", + "@aws-sdk/middleware-recursion-detection": "3.433.0", + "@aws-sdk/middleware-user-agent": "3.433.0", + "@aws-sdk/region-config-resolver": "3.433.0", + "@aws-sdk/types": "3.433.0", + "@aws-sdk/util-endpoints": "3.433.0", + "@aws-sdk/util-user-agent-browser": "3.433.0", + "@aws-sdk/util-user-agent-node": "3.433.0", + "@smithy/config-resolver": "^2.0.16", + "@smithy/fetch-http-handler": "^2.2.4", + "@smithy/hash-node": "^2.0.12", + "@smithy/invalid-dependency": "^2.0.12", + "@smithy/middleware-content-length": "^2.0.14", + "@smithy/middleware-endpoint": "^2.1.3", + "@smithy/middleware-retry": "^2.0.18", + "@smithy/middleware-serde": "^2.0.12", + "@smithy/middleware-stack": "^2.0.6", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/node-http-handler": "^2.1.8", + "@smithy/protocol-http": "^3.0.8", + "@smithy/smithy-client": "^2.1.12", + "@smithy/types": "^2.4.0", + "@smithy/url-parser": "^2.0.12", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.16", + "@smithy/util-defaults-mode-node": "^2.0.21", + "@smithy/util-retry": "^2.0.5", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": { + "version": "3.435.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.435.0.tgz", + "integrity": "sha512-xenshHn87b4cv45ntRgTQqeGk3H7Rrs7Br63cejFG+6ZJw7JRiz1g8EL+pIUEYyWHPYwDG0493ylxwf7p8XqaQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.435.0", + "@aws-sdk/middleware-host-header": "3.433.0", + "@aws-sdk/middleware-logger": "3.433.0", + "@aws-sdk/middleware-recursion-detection": "3.433.0", + "@aws-sdk/middleware-sdk-sts": "3.433.0", + "@aws-sdk/middleware-signing": "3.433.0", + "@aws-sdk/middleware-user-agent": "3.433.0", + "@aws-sdk/region-config-resolver": "3.433.0", + "@aws-sdk/types": "3.433.0", + "@aws-sdk/util-endpoints": "3.433.0", + "@aws-sdk/util-user-agent-browser": "3.433.0", + "@aws-sdk/util-user-agent-node": "3.433.0", + "@smithy/config-resolver": "^2.0.16", + "@smithy/fetch-http-handler": "^2.2.4", + "@smithy/hash-node": "^2.0.12", + "@smithy/invalid-dependency": "^2.0.12", + "@smithy/middleware-content-length": "^2.0.14", + "@smithy/middleware-endpoint": "^2.1.3", + "@smithy/middleware-retry": "^2.0.18", + "@smithy/middleware-serde": "^2.0.12", + "@smithy/middleware-stack": "^2.0.6", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/node-http-handler": "^2.1.8", + "@smithy/protocol-http": "^3.0.8", + "@smithy/smithy-client": "^2.1.12", + "@smithy/types": "^2.4.0", + "@smithy/url-parser": "^2.0.12", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.16", + "@smithy/util-defaults-mode-node": "^2.0.21", + "@smithy/util-retry": "^2.0.5", + "@smithy/util-utf8": "^2.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.433.0.tgz", + "integrity": "sha512-Vl7Qz5qYyxBurMn6hfSiNJeUHSqfVUlMt0C1Bds3tCkl3IzecRWwyBOlxtxO3VCrgVeW3HqswLzCvhAFzPH6nQ==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.435.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.435.0.tgz", + "integrity": "sha512-YHXftGxQ2UDaIyJ2F4ZbyU52MWyWZ9dFG9oKlnA0qMPF7AIH+GtH3X+oFGC0lCAi4zx4Zd26gFlkoqupVy1HbA==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.433.0", + "@aws-sdk/credential-provider-process": "3.433.0", + "@aws-sdk/credential-provider-sso": "3.435.0", + "@aws-sdk/credential-provider-web-identity": "3.433.0", + "@aws-sdk/types": "3.433.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.435.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.435.0.tgz", + "integrity": "sha512-58sOsgBzkmhyGAvTRkI/OPe+hhwsbbO1iuoyFPzFcfbU90S9NSN4BkRnvcgphbckBwKy+BIF0wP2fk/gF0CdEA==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.433.0", + "@aws-sdk/credential-provider-ini": "3.435.0", + "@aws-sdk/credential-provider-process": "3.433.0", + "@aws-sdk/credential-provider-sso": "3.435.0", + "@aws-sdk/credential-provider-web-identity": "3.433.0", + "@aws-sdk/types": "3.433.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.433.0.tgz", + "integrity": "sha512-W7FcGlQjio9Y/PepcZGRyl5Bpwb0uWU7qIUCh+u4+q2mW4D5ZngXg8V/opL9/I/p4tUH9VXZLyLGwyBSkdhL+A==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.435.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.435.0.tgz", + "integrity": "sha512-WPt/7efTM0lvHsCh+OzRp79wIatkCTnCoYcp4kCHIR+aq9Z9vXICPIhmSO4okGkHnlxd/7UuNdld1BoZkT9oRA==", + "dependencies": { + "@aws-sdk/client-sso": "3.435.0", + "@aws-sdk/token-providers": "3.435.0", + "@aws-sdk/types": "3.433.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.433.0.tgz", + "integrity": "sha512-RlwjP1I5wO+aPpwyCp23Mk8nmRbRL33hqRASy73c4JA2z2YiRua+ryt6MalIxehhwQU6xvXUKulJnPG9VaMFZg==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.433.0.tgz", + "integrity": "sha512-mBTq3UWv1UzeHG+OfUQ2MB/5GEkt5LTKFaUqzL7ESwzW8XtpBgXnjZvIwu3Vcd3sEetMwijwaGiJhY0ae/YyaA==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.433.0.tgz", + "integrity": "sha512-We346Fb5xGonTGVZC9Nvqtnqy74VJzYuTLLiuuftA5sbNzftBDy/22QCfvYSTOAl3bvif+dkDUzQY2ihc5PwOQ==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.433.0.tgz", + "integrity": "sha512-HEvYC9PQlWY/ccUYtLvAlwwf1iCif2TSAmLNr3YTBRVa98x6jKL0hlCrHWYklFeqOGSKy6XhE+NGJMUII0/HaQ==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.433.0.tgz", + "integrity": "sha512-ORYbJnBejUyonFl5FwIqhvI3Cq6sAp9j+JpkKZtFNma9tFPdrhmYgfCeNH32H/wGTQV/tUoQ3luh0gA4cuk6DA==", + "dependencies": { + "@aws-sdk/middleware-signing": "3.433.0", + "@aws-sdk/types": "3.433.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-signing": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.433.0.tgz", + "integrity": "sha512-jxPvt59NZo/epMNLNTu47ikmP8v0q217I6bQFGJG7JVFnfl36zDktMwGw+0xZR80qiK47/2BWrNpta61Zd2FxQ==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.4.0", + "@smithy/util-middleware": "^2.0.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.433.0.tgz", + "integrity": "sha512-jMgA1jHfisBK4oSjMKrtKEZf0sl2vzADivkFmyZFzORpSZxBnF6hC21RjaI+70LJLcc9rSCzLgcoz5lHb9LLDg==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@aws-sdk/util-endpoints": "3.433.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "version": "3.435.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.435.0.tgz", + "integrity": "sha512-JZKqsuoK321ozp2ufGmjfpbAqtK1tYnLn0PaePWjvDL48B5A5jGNqFyP3/tg7LFP7vTp9O3pJ7ln0QLh8FpsjQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.433.0", + "@aws-sdk/middleware-logger": "3.433.0", + "@aws-sdk/middleware-recursion-detection": "3.433.0", + "@aws-sdk/middleware-user-agent": "3.433.0", + "@aws-sdk/region-config-resolver": "3.433.0", + "@aws-sdk/types": "3.433.0", + "@aws-sdk/util-endpoints": "3.433.0", + "@aws-sdk/util-user-agent-browser": "3.433.0", + "@aws-sdk/util-user-agent-node": "3.433.0", + "@smithy/config-resolver": "^2.0.16", + "@smithy/fetch-http-handler": "^2.2.4", + "@smithy/hash-node": "^2.0.12", + "@smithy/invalid-dependency": "^2.0.12", + "@smithy/middleware-content-length": "^2.0.14", + "@smithy/middleware-endpoint": "^2.1.3", + "@smithy/middleware-retry": "^2.0.18", + "@smithy/middleware-serde": "^2.0.12", + "@smithy/middleware-stack": "^2.0.6", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/node-http-handler": "^2.1.8", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/smithy-client": "^2.1.12", + "@smithy/types": "^2.4.0", + "@smithy/url-parser": "^2.0.12", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.16", + "@smithy/util-defaults-mode-node": "^2.0.21", + "@smithy/util-retry": "^2.0.5", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.433.0.tgz", + "integrity": "sha512-LFNUh9FH7RMtYjSjPGz9lAJQMzmJ3RcXISzc5X5k2R/9mNwMK7y1k2VAfvx+RbuDbll6xwsXlgv6QHcxVdF2zw==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.433.0.tgz", + "integrity": "sha512-2Cf/Lwvxbt5RXvWFXrFr49vXv0IddiUwrZoAiwhDYxvsh+BMnh+NUFot+ZQaTrk/8IPZVDeLPWZRdVy00iaVXQ==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/types": "^2.4.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.433.0.tgz", + "integrity": "sha512-yT1tO4MbbsUBLl5+S+jVv8wxiAtP5TKjKib9B2KQ2x0OtWWTrIf2o+IZK8va+zQqdV4MVMjezdxdE20hOdB4yQ==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/abort-controller": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.12.tgz", + "integrity": "sha512-YIJyefe1mi3GxKdZxEBEuzYOeQ9xpYfqnFmWzojCssRAuR7ycxwpoRQgp965vuW426xUAQhCV5rCaWElQ7XsaA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/config-resolver": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.16.tgz", + "integrity": "sha512-1k+FWHQDt2pfpXhJsOmNMmlAZ3NUQ98X5tYsjQhVGq+0X6cOBMhfh6Igd0IX3Ut6lEO6DQAdPMI/blNr3JZfMQ==", + "dependencies": { + "@smithy/node-config-provider": "^2.1.3", + "@smithy/types": "^2.4.0", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/credential-provider-imds": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.18.tgz", + "integrity": "sha512-QnPBi6D2zj6AHJdUTo5zXmk8vwHJ2bNevhcVned1y+TZz/OI5cizz5DsYNkqFUIDn8tBuEyKNgbmKVNhBbuY3g==", + "dependencies": { + "@smithy/node-config-provider": "^2.1.3", + "@smithy/property-provider": "^2.0.13", + "@smithy/types": "^2.4.0", + "@smithy/url-parser": "^2.0.12", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/eventstream-codec": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.12.tgz", + "integrity": "sha512-ZZQLzHBJkbiAAdj2C5K+lBlYp/XJ+eH2uy+jgJgYIFW/o5AM59Hlj7zyI44/ZTDIQWmBxb3EFv/c5t44V8/g8A==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.4.0", + "@smithy/util-hex-encoding": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/fetch-http-handler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.4.tgz", + "integrity": "sha512-gIPRFEGi+c6V52eauGKrjDzPWF2Cu7Z1r5F8A3j2wcwz25sPG/t8kjsbEhli/tS/2zJp/ybCZXe4j4ro3yv/HA==", + "dependencies": { + "@smithy/protocol-http": "^3.0.8", + "@smithy/querystring-builder": "^2.0.12", + "@smithy/types": "^2.4.0", + "@smithy/util-base64": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/hash-node": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.12.tgz", + "integrity": "sha512-fDZnTr5j9t5qcbeJ037aMZXxMka13Znqwrgy3PAqYj6Dm3XHXHftTH3q+NWgayUxl1992GFtQt1RuEzRMy3NnQ==", + "dependencies": { + "@smithy/types": "^2.4.0", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/invalid-dependency": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.12.tgz", + "integrity": "sha512-p5Y+iMHV3SoEpy3VSR7mifbreHQwVSvHSAz/m4GdoXfOzKzaYC8hYv10Ks7Deblkf7lhas8U+lAp9ThbBM+ZXA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/middleware-content-length": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.14.tgz", + "integrity": "sha512-poUNgKTw9XwPXfX9nEHpVgrMNVpaSMZbshqvPxFVoalF4wp6kRzYKOfdesSVectlQ51VtigoLfbXcdyPwvxgTg==", + "dependencies": { + "@smithy/protocol-http": "^3.0.8", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/middleware-endpoint": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.1.3.tgz", + "integrity": "sha512-ZrQ0/YX6hNVTxqMEHtEaDbDv6pNeEji/a5Vk3HuFC5R3ZY8lfoATyxmOGxBVYnF3NUvZLNC7umEv1WzWGWvCGQ==", + "dependencies": { + "@smithy/middleware-serde": "^2.0.12", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.2.2", + "@smithy/types": "^2.4.0", + "@smithy/url-parser": "^2.0.12", + "@smithy/util-middleware": "^2.0.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/middleware-retry": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.18.tgz", + "integrity": "sha512-VyrHQRldGSb3v9oFOB5yPxmLT7U2sQic2ytylOnYlnsmVOLlFIaI6sW22c+w2675yq+XZ6HOuzV7x2OBYCWRNA==", + "dependencies": { + "@smithy/node-config-provider": "^2.1.3", + "@smithy/protocol-http": "^3.0.8", + "@smithy/service-error-classification": "^2.0.5", + "@smithy/types": "^2.4.0", + "@smithy/util-middleware": "^2.0.5", + "@smithy/util-retry": "^2.0.5", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/middleware-serde": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.12.tgz", + "integrity": "sha512-IBeco157lIScecq2Z+n0gq56i4MTnfKxS7rbfrAORveDJgnbBAaEQgYqMqp/cYqKrpvEXcyTjwKHrBjCCIZh2A==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/middleware-stack": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.6.tgz", + "integrity": "sha512-YSvNZeOKWLJ0M/ycxwDIe2Ztkp6Qixmcml1ggsSv2fdHKGkBPhGrX5tMzPGMI1yyx55UEYBi2OB4s+RriXX48A==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/node-config-provider": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.3.tgz", + "integrity": "sha512-J6lXvRHGVnSX3n1PYi+e1L5HN73DkkJpUviV3Ebf+8wSaIjAf+eVNbzyvh/S5EQz7nf4KVfwbD5vdoZMAthAEQ==", + "dependencies": { + "@smithy/property-provider": "^2.0.13", + "@smithy/shared-ini-file-loader": "^2.2.2", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/node-http-handler": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.8.tgz", + "integrity": "sha512-KZylM7Wff/So5SmCiwg2kQNXJ+RXgz34wkxS7WNwIUXuZrZZpY/jKJCK+ZaGyuESDu3TxcaY+zeYGJmnFKbQsA==", + "dependencies": { + "@smithy/abort-controller": "^2.0.12", + "@smithy/protocol-http": "^3.0.8", + "@smithy/querystring-builder": "^2.0.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/property-provider": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.13.tgz", + "integrity": "sha512-VJqUf2CbsQX6uUiC5dUPuoEATuFjkbkW3lJHbRnpk9EDC9X+iKqhfTK+WP+lve5EQ9TcCI1Q6R7hrg41FyC54w==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/protocol-http": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.8.tgz", + "integrity": "sha512-SHJvYeWq8q0FK8xHk+xjV9dzDUDjFMT+G1pZbV+XB6OVoac/FSVshlMNPeUJ8AmSkcDKHRu5vASnRqZHgD3qhw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/querystring-builder": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.12.tgz", + "integrity": "sha512-cDbF07IuCjiN8CdGvPzfJjXIrmDSelScRfyJYrYBNBbKl2+k7QD/KqiHhtRyEKgID5mmEVrV6KE6L/iPJ98sFw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "@smithy/util-uri-escape": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/querystring-parser": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.12.tgz", + "integrity": "sha512-fytyTcXaMzPBuNtPlhj5v6dbl4bJAnwKZFyyItAGt4Tgm9HFPZNo7a9r1SKPr/qdxUEBzvL9Rh+B9SkTX3kFxg==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/service-error-classification": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.5.tgz", + "integrity": "sha512-M0SeJnEgD2ywJyV99Fb1yKFzmxDe9JfpJiYTVSRMyRLc467BPU0qsuuDPzMCdB1mU8M8u1rVOdkqdoyFN8UFTw==", + "dependencies": { + "@smithy/types": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/shared-ini-file-loader": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.2.tgz", + "integrity": "sha512-noyQUPn7b1M8uB0GEXc/Zyxq+5K2b7aaqWnLp+hgJ7+xu/FCvtyWy5eWLDjQEsHnAet2IZhS5QF8872OR69uNg==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/signature-v4": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.12.tgz", + "integrity": "sha512-6Kc2lCZEVmb1nNYngyNbWpq0d82OZwITH11SW/Q0U6PX5fH7B2cIcFe7o6eGEFPkTZTP8itTzmYiGcECL0D0Lw==", + "dependencies": { + "@smithy/eventstream-codec": "^2.0.12", + "@smithy/is-array-buffer": "^2.0.0", + "@smithy/types": "^2.4.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-middleware": "^2.0.5", + "@smithy/util-uri-escape": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/smithy-client": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.12.tgz", + "integrity": "sha512-XXqhridfkKnpj+lt8vM6HRlZbqUAqBjVC74JIi13F/AYQd/zTj9SOyGfxnbp4mjY9q28LityxIuV8CTinr9r5w==", + "dependencies": { + "@smithy/middleware-stack": "^2.0.6", + "@smithy/types": "^2.4.0", + "@smithy/util-stream": "^2.0.17", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/url-parser": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.12.tgz", + "integrity": "sha512-qgkW2mZqRvlNUcBkxYB/gYacRaAdck77Dk3/g2iw0S9F0EYthIS3loGfly8AwoWpIvHKhkTsCXXQfzksgZ4zIA==", + "dependencies": { + "@smithy/querystring-parser": "^2.0.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-base64": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", + "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-body-length-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", + "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-body-length-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", + "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-config-provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", + "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.16.tgz", + "integrity": "sha512-Uv5Cu8nVkuvLn0puX+R9zWbSNpLIR3AxUlPoLJ7hC5lvir8B2WVqVEkJLwtixKAncVLasnTVjPDCidtAUTGEQw==", + "dependencies": { + "@smithy/property-provider": "^2.0.13", + "@smithy/smithy-client": "^2.1.12", + "@smithy/types": "^2.4.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-defaults-mode-node": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.21.tgz", + "integrity": "sha512-cUEsttVZ79B7Al2rWK2FW03HBpD9LyuqFtm+1qFty5u9sHSdesr215gS2Ln53fTopNiPgeXpdoM3IgjvIO0rJw==", + "dependencies": { + "@smithy/config-resolver": "^2.0.16", + "@smithy/credential-provider-imds": "^2.0.18", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/property-provider": "^2.0.13", + "@smithy/smithy-client": "^2.1.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-middleware": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.5.tgz", + "integrity": "sha512-1lyT3TcaMJQe+OFfVI+TlomDkPuVzb27NZYdYtmSTltVmLaUjdCyt4KE+OH1CnhZKsz4/cdCL420Lg9UH5Z2Mw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.5.tgz", + "integrity": "sha512-x3t1+MQAJ6QONk3GTbJNcugCFDVJ+Bkro5YqQQK1EyVesajNDqxFtCx9WdOFNGm/Cbm7tUdwVEmfKQOJoU2Vtw==", + "dependencies": { + "@smithy/service-error-classification": "^2.0.5", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-stream": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.17.tgz", + "integrity": "sha512-fP/ZQ27rRvHsqItds8yB7jerwMpZFTL3QqbQbidUiG0+mttMoKdP0ZqnvM8UK5q0/dfc3/pN7g4XKPXOU7oRWw==", + "dependencies": { + "@smithy/fetch-http-handler": "^2.2.4", + "@smithy/node-http-handler": "^2.1.8", + "@smithy/types": "^2.4.0", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-uri-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", + "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-sso": { "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.370.0.tgz", @@ -857,6 +1818,244 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.433.0.tgz", + "integrity": "sha512-Lk1xIu2tWTRa1zDw5hCF1RrpWQYSodUhrS/q3oKz8IAoFqEy+lNaD5jx+fycuZb5EkE4IzWysT+8wVkd0mAnOg==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "@smithy/node-config-provider": "^2.1.3", + "@smithy/protocol-http": "^3.0.8", + "@smithy/types": "^2.4.0", + "@smithy/util-config-provider": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@smithy/node-config-provider": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.3.tgz", + "integrity": "sha512-J6lXvRHGVnSX3n1PYi+e1L5HN73DkkJpUviV3Ebf+8wSaIjAf+eVNbzyvh/S5EQz7nf4KVfwbD5vdoZMAthAEQ==", + "dependencies": { + "@smithy/property-provider": "^2.0.13", + "@smithy/shared-ini-file-loader": "^2.2.2", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@smithy/property-provider": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.13.tgz", + "integrity": "sha512-VJqUf2CbsQX6uUiC5dUPuoEATuFjkbkW3lJHbRnpk9EDC9X+iKqhfTK+WP+lve5EQ9TcCI1Q6R7hrg41FyC54w==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@smithy/protocol-http": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.8.tgz", + "integrity": "sha512-SHJvYeWq8q0FK8xHk+xjV9dzDUDjFMT+G1pZbV+XB6OVoac/FSVshlMNPeUJ8AmSkcDKHRu5vASnRqZHgD3qhw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@smithy/shared-ini-file-loader": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.2.tgz", + "integrity": "sha512-noyQUPn7b1M8uB0GEXc/Zyxq+5K2b7aaqWnLp+hgJ7+xu/FCvtyWy5eWLDjQEsHnAet2IZhS5QF8872OR69uNg==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@smithy/util-config-provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", + "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.433.0.tgz", + "integrity": "sha512-Uq2rPIsjz0CR2sulM/HyYr5WiqiefrSRLdwUZuA7opxFSfE808w5DBWSprHxbH3rbDSQR4nFiOiVYIH8Eth7nA==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@smithy/protocol-http": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.8.tgz", + "integrity": "sha512-SHJvYeWq8q0FK8xHk+xjV9dzDUDjFMT+G1pZbV+XB6OVoac/FSVshlMNPeUJ8AmSkcDKHRu5vASnRqZHgD3qhw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.433.0.tgz", + "integrity": "sha512-Ptssx373+I7EzFUWjp/i/YiNFt6I6sDuRHz6DOUR9nmmRTlHHqmdcBXlJL2d9wwFxoBRCN8/PXGsTc/DJ4c95Q==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/types": "3.433.0", + "@smithy/is-array-buffer": "^2.0.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/types": "^2.4.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/protocol-http": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.8.tgz", + "integrity": "sha512-SHJvYeWq8q0FK8xHk+xjV9dzDUDjFMT+G1pZbV+XB6OVoac/FSVshlMNPeUJ8AmSkcDKHRu5vASnRqZHgD3qhw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.370.0.tgz", @@ -872,6 +2071,42 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.433.0.tgz", + "integrity": "sha512-2YD860TGntwZifIUbxm+lFnNJJhByR/RB/+fV1I8oGKg+XX2rZU+94pRfHXRywoZKlCA0L+LGDA1I56jxrB9sw==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/middleware-logger": { "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.370.0.tgz", @@ -901,6 +2136,222 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.433.0.tgz", + "integrity": "sha512-mkn3DiSuMVh4NTLsduC42Av+ApcOor52LMoQY0Wc6M5Mx7Xd05U+G1j8sjI9n/1bs5cZ/PoeRYJ/9bL1Xxznnw==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/smithy-client": "^2.1.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/abort-controller": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.12.tgz", + "integrity": "sha512-YIJyefe1mi3GxKdZxEBEuzYOeQ9xpYfqnFmWzojCssRAuR7ycxwpoRQgp965vuW426xUAQhCV5rCaWElQ7XsaA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/fetch-http-handler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.4.tgz", + "integrity": "sha512-gIPRFEGi+c6V52eauGKrjDzPWF2Cu7Z1r5F8A3j2wcwz25sPG/t8kjsbEhli/tS/2zJp/ybCZXe4j4ro3yv/HA==", + "dependencies": { + "@smithy/protocol-http": "^3.0.8", + "@smithy/querystring-builder": "^2.0.12", + "@smithy/types": "^2.4.0", + "@smithy/util-base64": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/middleware-stack": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.6.tgz", + "integrity": "sha512-YSvNZeOKWLJ0M/ycxwDIe2Ztkp6Qixmcml1ggsSv2fdHKGkBPhGrX5tMzPGMI1yyx55UEYBi2OB4s+RriXX48A==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/node-http-handler": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.8.tgz", + "integrity": "sha512-KZylM7Wff/So5SmCiwg2kQNXJ+RXgz34wkxS7WNwIUXuZrZZpY/jKJCK+ZaGyuESDu3TxcaY+zeYGJmnFKbQsA==", + "dependencies": { + "@smithy/abort-controller": "^2.0.12", + "@smithy/protocol-http": "^3.0.8", + "@smithy/querystring-builder": "^2.0.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/protocol-http": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.8.tgz", + "integrity": "sha512-SHJvYeWq8q0FK8xHk+xjV9dzDUDjFMT+G1pZbV+XB6OVoac/FSVshlMNPeUJ8AmSkcDKHRu5vASnRqZHgD3qhw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/querystring-builder": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.12.tgz", + "integrity": "sha512-cDbF07IuCjiN8CdGvPzfJjXIrmDSelScRfyJYrYBNBbKl2+k7QD/KqiHhtRyEKgID5mmEVrV6KE6L/iPJ98sFw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "@smithy/util-uri-escape": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/smithy-client": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.12.tgz", + "integrity": "sha512-XXqhridfkKnpj+lt8vM6HRlZbqUAqBjVC74JIi13F/AYQd/zTj9SOyGfxnbp4mjY9q28LityxIuV8CTinr9r5w==", + "dependencies": { + "@smithy/middleware-stack": "^2.0.6", + "@smithy/types": "^2.4.0", + "@smithy/util-stream": "^2.0.17", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-base64": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", + "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-stream": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.17.tgz", + "integrity": "sha512-fP/ZQ27rRvHsqItds8yB7jerwMpZFTL3QqbQbidUiG0+mttMoKdP0ZqnvM8UK5q0/dfc3/pN7g4XKPXOU7oRWw==", + "dependencies": { + "@smithy/fetch-http-handler": "^2.2.4", + "@smithy/node-http-handler": "^2.1.8", + "@smithy/types": "^2.4.0", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-uri-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", + "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/middleware-sdk-sts": { "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.370.0.tgz", @@ -934,6 +2385,42 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.433.0.tgz", + "integrity": "sha512-2AMaPx0kYfCiekxoL7aqFqSSoA9du+yI4zefpQNLr+1cZOerYiDxdsZ4mbqStR1CVFaX6U6hrYokXzjInsvETw==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.370.0.tgz", @@ -950,6 +2437,241 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.433.0.tgz", + "integrity": "sha512-xpjRjCZW+CDFdcMmmhIYg81ST5UAnJh61IHziQEk0FXONrg4kjyYPZAOjEdzXQ+HxJQuGQLKPhRdzxmQnbX7pg==", + "dependencies": { + "@smithy/node-config-provider": "^2.1.3", + "@smithy/types": "^2.4.0", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/node-config-provider": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.3.tgz", + "integrity": "sha512-J6lXvRHGVnSX3n1PYi+e1L5HN73DkkJpUviV3Ebf+8wSaIjAf+eVNbzyvh/S5EQz7nf4KVfwbD5vdoZMAthAEQ==", + "dependencies": { + "@smithy/property-provider": "^2.0.13", + "@smithy/shared-ini-file-loader": "^2.2.2", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/property-provider": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.13.tgz", + "integrity": "sha512-VJqUf2CbsQX6uUiC5dUPuoEATuFjkbkW3lJHbRnpk9EDC9X+iKqhfTK+WP+lve5EQ9TcCI1Q6R7hrg41FyC54w==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/shared-ini-file-loader": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.2.tgz", + "integrity": "sha512-noyQUPn7b1M8uB0GEXc/Zyxq+5K2b7aaqWnLp+hgJ7+xu/FCvtyWy5eWLDjQEsHnAet2IZhS5QF8872OR69uNg==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/util-config-provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", + "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/util-middleware": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.5.tgz", + "integrity": "sha512-1lyT3TcaMJQe+OFfVI+TlomDkPuVzb27NZYdYtmSTltVmLaUjdCyt4KE+OH1CnhZKsz4/cdCL420Lg9UH5Z2Mw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.433.0.tgz", + "integrity": "sha512-wl2j1dos4VOKFawbapPm/0CNa3cIgpJXbEx+sp+DI3G8tSuP3c5UGtm0pXjM85egxZulhHVK1RVde0iD8j63pQ==", + "dependencies": { + "@aws-sdk/types": "3.433.0", + "@smithy/protocol-http": "^3.0.8", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@aws-sdk/types": { + "version": "3.433.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", + "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/eventstream-codec": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.12.tgz", + "integrity": "sha512-ZZQLzHBJkbiAAdj2C5K+lBlYp/XJ+eH2uy+jgJgYIFW/o5AM59Hlj7zyI44/ZTDIQWmBxb3EFv/c5t44V8/g8A==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.4.0", + "@smithy/util-hex-encoding": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/protocol-http": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.8.tgz", + "integrity": "sha512-SHJvYeWq8q0FK8xHk+xjV9dzDUDjFMT+G1pZbV+XB6OVoac/FSVshlMNPeUJ8AmSkcDKHRu5vASnRqZHgD3qhw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/signature-v4": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.12.tgz", + "integrity": "sha512-6Kc2lCZEVmb1nNYngyNbWpq0d82OZwITH11SW/Q0U6PX5fH7B2cIcFe7o6eGEFPkTZTP8itTzmYiGcECL0D0Lw==", + "dependencies": { + "@smithy/eventstream-codec": "^2.0.12", + "@smithy/is-array-buffer": "^2.0.0", + "@smithy/types": "^2.4.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-middleware": "^2.0.5", + "@smithy/util-uri-escape": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/util-middleware": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.5.tgz", + "integrity": "sha512-1lyT3TcaMJQe+OFfVI+TlomDkPuVzb27NZYdYtmSTltVmLaUjdCyt4KE+OH1CnhZKsz4/cdCL420Lg9UH5Z2Mw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/util-uri-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", + "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/token-providers": { "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.370.0.tgz", @@ -971,7 +2693,6 @@ "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", - "optional": true, "dependencies": { "@smithy/types": "^1.1.0", "tslib": "^2.5.0" @@ -980,6 +2701,17 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.310.0.tgz", + "integrity": "sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.370.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.370.0.tgz", @@ -997,7 +2729,6 @@ "version": "3.310.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", - "optional": true, "dependencies": { "tslib": "^2.5.0" }, @@ -1044,11 +2775,21 @@ "version": "3.259.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "optional": true, "dependencies": { "tslib": "^2.3.1" } }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.310.0.tgz", + "integrity": "sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -1191,16 +2932,16 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", - "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", + "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -1229,9 +2970,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", - "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", + "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -1275,22 +3016,22 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -1350,12 +3091,12 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", - "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { @@ -1518,12 +3259,13 @@ } }, "node_modules/@babel/plugin-proposal-export-default-from": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz", - "integrity": "sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.23.3.tgz", + "integrity": "sha512-Q23MpLZfSGZL1kU7fWqV262q65svLSCIP5kZ/JCW/rKTCm/FrLjpvEd2kfUYMVeHh4QhV/xzyoRAHWrAZJrE3Q==", + "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-default-from": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-default-from": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -1670,11 +3412,12 @@ } }, "node_modules/@babel/plugin-syntax-export-default-from": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz", - "integrity": "sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.23.3.tgz", + "integrity": "sha512-KeENO5ck1IeZ/l2lFZNy+mpobV3D2Zy5C1YFnWm+YuY5mQiAWc4yAp13dqgguwsBsFVLh4LPCEqCa5qW13N+hw==", + "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2566,15 +4309,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz", - "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.7.tgz", + "integrity": "sha512-fa0hnfmiXc9fq/weK34MUV0drz2pOL/vfKWvN7Qw127hiUPabFCUMgAbYWcchRzMJit4o5ARsK/s+5h0249pLw==", "dependencies": { - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "babel-plugin-polyfill-corejs2": "^0.4.7", + "babel-plugin-polyfill-corejs3": "^0.8.7", + "babel-plugin-polyfill-regenerator": "^0.5.4", "semver": "^6.3.1" }, "engines": { @@ -2894,11 +4637,11 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", + "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -2916,6 +4659,18 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/runtime-corejs2/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -3037,6 +4792,118 @@ "node": ">=0.1.90" } }, + "node_modules/@cypress/code-coverage": { + "version": "3.12.15", + "resolved": "https://registry.npmjs.org/@cypress/code-coverage/-/code-coverage-3.12.15.tgz", + "integrity": "sha512-HnOxIAl/8gEI/Cyg4bbipe7LEJ/EsIMYSm7jwdlYT1XD9e8ZO7tSJUxX1EV5GCPbwjI8ZS2shrwxGzt3pYH+UQ==", + "dev": true, + "dependencies": { + "@cypress/webpack-preprocessor": "^6.0.0", + "chalk": "4.1.2", + "dayjs": "1.11.10", + "debug": "4.3.4", + "execa": "4.1.0", + "globby": "11.1.0", + "istanbul-lib-coverage": "^3.0.0", + "js-yaml": "4.1.0", + "nyc": "15.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.1", + "@babel/preset-env": "^7.0.0", + "babel-loader": "^8.3 || ^9", + "cypress": "*", + "webpack": "^4 || ^5" + } + }, + "node_modules/@cypress/code-coverage/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@cypress/code-coverage/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@cypress/code-coverage/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@cypress/code-coverage/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@cypress/code-coverage/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@cypress/code-coverage/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@cypress/code-coverage/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@cypress/code-coverage/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@cypress/request": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.0.tgz", @@ -3095,6 +4962,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@cypress/webpack-preprocessor": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@cypress/webpack-preprocessor/-/webpack-preprocessor-6.0.1.tgz", + "integrity": "sha512-WVNeFVSnFKxE3WZNRIriduTgqJRpevaiJIPlfqYTTzfXRD7X1Pv4woDE+G4caPV9bJqVKmVFiwzrXMRNeJxpxA==", + "dev": true, + "dependencies": { + "bluebird": "3.7.1", + "debug": "^4.3.4", + "lodash": "^4.17.20" + }, + "peerDependencies": { + "@babel/core": "^7.0.1", + "@babel/preset-env": "^7.0.0", + "babel-loader": "^8.3 || ^9", + "webpack": "^4 || ^5" + } + }, + "node_modules/@cypress/webpack-preprocessor/node_modules/bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "dev": true + }, "node_modules/@cypress/xvfb": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", @@ -4237,6 +6127,92 @@ "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", "dev": true }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -6270,6 +8246,58 @@ "node": ">=14.0.0" } }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.0.0.tgz", + "integrity": "sha512-k+J4GHJsMSAIQPChGBrjEmGS+WbPonCXesoqP9fynIqjn7rdOThdH8FAeCmokP9mxTYKQAKoHCLPzNlm6gh7Wg==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.0.0.tgz", + "integrity": "sha512-HM8V2Rp1y8+1343tkZUKZllFhEQPNmpNdgFAncbTsxkZ18/gqjk23XXv3qGyXWp412f3o43ZZ1UZHVcHrpRnCQ==", + "dependencies": { + "@smithy/util-base64": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native/node_modules/@smithy/util-base64": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", + "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@smithy/config-resolver": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-1.1.0.tgz", @@ -6313,6 +8341,123 @@ "tslib": "^2.5.0" } }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.0.12.tgz", + "integrity": "sha512-0pi8QlU/pwutNshoeJcbKR1p7Ie5STd8UFAMX5xhSoSJjNlxIv/OsHbF023jscMRN2Prrqd6ToGgdCnsZVQjvg==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^2.0.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.0.12.tgz", + "integrity": "sha512-I0XfwQkIX3gAnbrU5rLMkBSjTM9DHttdbLwf12CXmj7SSI5dT87PxtKLRrZGanaCMbdf2yCep+MW5/4M7IbvQA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.0.12.tgz", + "integrity": "sha512-vf1vMHGOkG3uqN9x1zKOhnvW/XgvhJXWqjV6zZiT2FMjlEayugQ1mzpSqr7uf89+BzjTzuZKERmOsEAmewLbxw==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^2.0.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.0.12.tgz", + "integrity": "sha512-xZ3ZNpCxIND+q+UCy7y1n1/5VQEYicgSTNCcPqsKawX+Vd+6OcFX7gUHMyPzL8cZr+GdmJuxNleqHlH4giK2tw==", + "dependencies": { + "@smithy/eventstream-codec": "^2.0.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal/node_modules/@smithy/eventstream-codec": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.12.tgz", + "integrity": "sha512-ZZQLzHBJkbiAAdj2C5K+lBlYp/XJ+eH2uy+jgJgYIFW/o5AM59Hlj7zyI44/ZTDIQWmBxb3EFv/c5t44V8/g8A==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.4.0", + "@smithy/util-hex-encoding": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal/node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@smithy/fetch-http-handler": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.1.0.tgz", @@ -6326,6 +8471,28 @@ "tslib": "^2.5.0" } }, + "node_modules/@smithy/hash-blob-browser": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.0.12.tgz", + "integrity": "sha512-riLnV16f27yyePX8UF0deRHAeccUK8SrOxyTykSTrnVkgS3DsjNapZtTbd8OGNKEbI60Ncdb5GwN3rHZudXvog==", + "dependencies": { + "@smithy/chunked-blob-reader": "^2.0.0", + "@smithy/chunked-blob-reader-native": "^2.0.0", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/hash-blob-browser/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@smithy/hash-node": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-1.1.0.tgz", @@ -6341,6 +8508,65 @@ "node": ">=14.0.0" } }, + "node_modules/@smithy/hash-stream-node": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.0.12.tgz", + "integrity": "sha512-x/DrSynPKrW0k00q7aZ/vy531a3mRw79mOajHp+cIF0TrA1SqEMFoy/B8X0XtoAtlJWt/vvgeDNqt/KAeaAqMw==", + "dependencies": { + "@smithy/types": "^2.4.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@smithy/invalid-dependency": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-1.1.0.tgz", @@ -6363,6 +8589,62 @@ "node": ">=14.0.0" } }, + "node_modules/@smithy/md5-js": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.0.12.tgz", + "integrity": "sha512-OgDt+Xnrw+W5z3MSl5KZZzebqmXrYl9UdbCiBYnnjErmNywwSjV6QB/Oic3/7hnsPniSU81n7Rvlhz2kH4EREQ==", + "dependencies": { + "@smithy/types": "^2.4.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@smithy/middleware-content-length": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-1.1.0.tgz", @@ -6580,7 +8862,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", - "optional": true, "dependencies": { "tslib": "^2.5.0" }, @@ -6771,6 +9052,42 @@ "node": ">=14.0.0" } }, + "node_modules/@smithy/util-waiter": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.0.12.tgz", + "integrity": "sha512-3sENmyVa1NnOPoiT2NCApPmu7ukP7S/v7kL9IxNmnygkDldn7/yK0TP42oPJLwB2k3mospNsSePIlqdXEUyPHA==", + "dependencies": { + "@smithy/abort-controller": "^2.0.12", + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-waiter/node_modules/@smithy/abort-controller": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.12.tgz", + "integrity": "sha512-YIJyefe1mi3GxKdZxEBEuzYOeQ9xpYfqnFmWzojCssRAuR7ycxwpoRQgp965vuW426xUAQhCV5rCaWElQ7XsaA==", + "dependencies": { + "@smithy/types": "^2.4.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-waiter/node_modules/@smithy/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", @@ -8071,6 +10388,18 @@ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/application-config-path": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.1.tgz", @@ -8116,6 +10445,12 @@ "node": ">=4" } }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -8356,68 +10691,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sdk": { - "version": "2.1422.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1422.0.tgz", - "integrity": "sha512-dBHCT42s2NOrZVSeKQ+I0kPsAPN0QhbKv05gQFQL79yGIOqLZawWos8vtKSYdp9wEOpiIRttB7m6usYJObKpWA==", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-sdk/node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/aws-sdk/node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "node_modules/aws-sdk/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/aws-sdk/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "node_modules/aws-sdk/node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -8548,6 +10821,22 @@ "object.assign": "^4.1.0" } }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/babel-plugin-lodash": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz", @@ -8575,12 +10864,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", - "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", + "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.2", + "@babel/helper-define-polyfill-provider": "^0.4.4", "semver": "^6.3.1" }, "peerDependencies": { @@ -8588,23 +10877,23 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", - "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", + "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.2", - "core-js-compat": "^3.31.0" + "@babel/helper-define-polyfill-provider": "^0.4.4", + "core-js-compat": "^3.33.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-corejs3/node_modules/core-js-compat": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", - "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", "dependencies": { - "browserslist": "^4.21.9" + "browserslist": "^4.22.2" }, "funding": { "type": "opencollective", @@ -8612,11 +10901,11 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", - "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", + "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.2" + "@babel/helper-define-polyfill-provider": "^0.4.4" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -8686,6 +10975,35 @@ "@babel/core": "^7.0.0" } }, + "node_modules/babel-preset-gatsby": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-3.13.0.tgz", + "integrity": "sha512-dkTg3j8K1FLXQvAAs3iQnL5rPVaWFqvBeWns0rOg7iijyXC63Ma5FI1Mp7aKfwXWDd29//hIZ1+DNpy2rrGiDg==", + "dependencies": { + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.20.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-classes": "^7.20.7", + "@babel/plugin-transform-runtime": "^7.19.6", + "@babel/plugin-transform-spread": "^7.20.7", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/runtime": "^7.20.13", + "babel-plugin-dynamic-import-node": "^2.3.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24", + "gatsby-core-utils": "^4.13.0", + "gatsby-legacy-polyfills": "^3.13.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.6", + "core-js": "^3.0.0" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -8871,8 +11189,7 @@ "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "optional": true + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" }, "node_modules/boxen": { "version": "5.1.2", @@ -8988,9 +11305,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "funding": [ { "type": "opencollective", @@ -9006,10 +11323,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -9273,6 +11590,36 @@ "node": ">=6" } }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -9333,9 +11680,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001517", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", - "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", + "version": "1.0.30001572", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", + "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", "funding": [ { "type": "opencollective", @@ -9960,16 +12307,6 @@ "lodash": ">=4.0" } }, - "node_modules/cloudinary/node_modules/core-js": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz", - "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -10446,16 +12783,19 @@ "dev": true }, "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.34.0.tgz", + "integrity": "sha512-aDdvlDder8QmY91H88GzNi9EtQi2TjvQhpCX6B1v/dAZHU1AuLgHvRh54RiOerpEhEW46Tkf+vgAViB/CWC0ag==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } }, "node_modules/core-js-compat": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", - "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.0.tgz", + "integrity": "sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw==", "dependencies": { "browserslist": "^4.21.5" }, @@ -11593,9 +13933,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==", + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", "dev": true }, "node_modules/debounce": { @@ -11888,6 +14228,30 @@ "node": ">=0.10.0" } }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-require-extensions/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -12450,9 +14814,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.470", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.470.tgz", - "integrity": "sha512-zZM48Lmy2FKWgqyvsX9XK+J6FfP7aCDUFLmgooLJzA7v1agCs/sxSoBpTIwDLhmbhpx9yJIxj2INig/ncjJRqg==" + "version": "1.4.616", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", + "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==" }, "node_modules/ellipsize": { "version": "0.1.0", @@ -12801,6 +15165,12 @@ "node": ">=0.10" } }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "node_modules/es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", @@ -13632,14 +16002,6 @@ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", @@ -14066,7 +16428,6 @@ "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "optional": true, "dependencies": { "strnum": "^1.0.5" }, @@ -14391,9 +16752,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "funding": [ { "type": "individual", @@ -14417,6 +16778,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -14753,6 +17127,26 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -15255,9 +17649,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/gatsby-core-utils": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-4.11.0.tgz", - "integrity": "sha512-W7pfrKgBchdk19g802IuPkCA2iJ69lRR1GzkfYjB8d1TuIQqf0l1z0lv7e+2kQqO+uQ5Yt3sGMMN2qMYMWfLXg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-4.13.0.tgz", + "integrity": "sha512-+oJJsADfcEnzpQpof+L5qtP4iSeMaEPn1QSjXENlg/go9Pi/4eqb+Nn3y3q8bC/zy4hMWFWrPdMJmdW581uNvA==", "dependencies": { "@babel/runtime": "^7.20.13", "ci-info": "2.0.0", @@ -15480,12 +17874,12 @@ } }, "node_modules/gatsby-legacy-polyfills": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/gatsby-legacy-polyfills/-/gatsby-legacy-polyfills-3.11.0.tgz", - "integrity": "sha512-3NvNmrmmng28MS4KYAUEd1Vip4B1VJCyeGMof8OfQlMPxZMijHmeasjFDf1l5HSTUsaHotNe7gdLqITTP9CAKQ==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/gatsby-legacy-polyfills/-/gatsby-legacy-polyfills-3.13.0.tgz", + "integrity": "sha512-nFZzq0+iv1+fGbDq1Pry2tFGOuj7VFXXTicxtWWaz0+vdE72Gp7HBGIjxlrcUPW709JUc4J2L7DTUG74sNWEFw==", "dependencies": { "@babel/runtime": "^7.20.13", - "core-js-compat": "3.30.2" + "core-js-compat": "3.31.0" } }, "node_modules/gatsby-link": { @@ -16616,22 +19010,6 @@ "node": ">=12.7.0" } }, - "node_modules/gatsby-source-s3": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/gatsby-source-s3/-/gatsby-source-s3-3.2.4.tgz", - "integrity": "sha512-T5t7+aE/tSSjf/SqfrinyCtJw2+t3D/c7OO5FwPmXcHiiOQPGcPou5NNRC8pczOfR8hrFMqptP3vahAPg842ww==", - "dependencies": { - "@babel/runtime": "^7.21.0", - "aws-sdk": "^2.1328.0", - "gatsby-source-filesystem": "^5.7.0" - }, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "gatsby": "^4.5.2 || ^5.0.0" - } - }, "node_modules/gatsby-telemetry": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-4.11.0.tgz", @@ -17022,35 +19400,6 @@ "follow-redirects": "^1.14.0" } }, - "node_modules/gatsby/node_modules/babel-preset-gatsby": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-3.11.0.tgz", - "integrity": "sha512-JKsFEeqQk6dvWGyqN8VPhxsWU7RohzILK5fxoSXQIk8MQnV/gHJSULju1FFH6DNpb85lgFGsgpU77X9/YPS7Sw==", - "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.20.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-classes": "^7.20.7", - "@babel/plugin-transform-runtime": "^7.19.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/runtime": "^7.20.13", - "babel-plugin-dynamic-import-node": "^2.3.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24", - "gatsby-core-utils": "^4.11.0", - "gatsby-legacy-polyfills": "^3.11.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.6", - "core-js": "^3.0.0" - } - }, "node_modules/gatsby/node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -17124,16 +19473,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/gatsby/node_modules/core-js": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", - "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/gatsby/node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -17520,6 +19859,15 @@ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", "dev": true }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", @@ -18417,6 +20765,12 @@ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/html-parse-stringify": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", @@ -19382,6 +21736,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -19870,6 +22225,191 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", @@ -19928,14 +22468,6 @@ "jiti": "bin/jiti.js" } }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/joi": { "version": "17.9.2", "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz", @@ -23249,11 +25781,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/msgpackr": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.5.tgz", - "integrity": "sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.1.tgz", + "integrity": "sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==", "optionalDependencies": { - "msgpackr-extract": "^3.0.1" + "msgpackr-extract": "^3.0.2" } }, "node_modules/msgpackr-extract": { @@ -23956,10 +26488,22 @@ "node": ">=0.10.0" } }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/node-stream-zip": { "version": "1.15.0", @@ -24156,6 +26700,141 @@ "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -24556,6 +27235,21 @@ "node": ">=6" } }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/package-json": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.0.tgz", @@ -26222,6 +28916,18 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -26481,15 +29187,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -26773,6 +29470,18 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "node_modules/react-datetime": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-datetime/-/react-datetime-3.2.0.tgz", + "integrity": "sha512-w5XdeNIGzBht9CadaZIJhKUhEcDTgH0XokKxGPCxeeJRYL7B3HIKA8CM6Q0xej2JFJt0n5d+zi3maMwaY3262A==", + "dependencies": { + "prop-types": "^15.5.7" + }, + "peerDependencies": { + "moment": "^2.16.0", + "react": "^16.5.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -28029,6 +30738,18 @@ "invariant": "^2.2.4" } }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/remark": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", @@ -29108,11 +31829,6 @@ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==" }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -29420,9 +32136,9 @@ "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" }, "node_modules/sharp": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.4.tgz", - "integrity": "sha512-exUnZewqVZC6UXqXuQ8fyJJv0M968feBi04jb9GcUHrWtkRoAKnbJt8IfwT4NJs7FskArbJ14JAFGVuooszoGg==", + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", "hasInstallScript": true, "dependencies": { "color": "^4.2.3", @@ -29932,6 +32648,38 @@ "memory-pager": "^1.0.2" } }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -30693,8 +33441,7 @@ "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "optional": true + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, "node_modules/strtok3": { "version": "6.3.0", @@ -31510,6 +34257,20 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -32579,9 +35340,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -32708,18 +35469,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -33453,26 +36202,6 @@ "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==" }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", diff --git a/site/gatsby-site/package.json b/site/gatsby-site/package.json index 66b33d7280..794393392f 100644 --- a/site/gatsby-site/package.json +++ b/site/gatsby-site/package.json @@ -6,7 +6,7 @@ "version": "0.2.0", "dependencies": { "@apollo/client": "^3.7.8", - "@babel/plugin-proposal-export-default-from": "^7.12.13", + "@aws-sdk/client-s3": "^3.435.0", "@billboard.js/react": "^1.0.1", "@bytemd/react": "^1.15.0", "@cloudinary/base": "^1.0.0-beta.4", @@ -54,7 +54,6 @@ "gatsby-source-filesystem": "^5.0.0", "gatsby-source-mongodb": "^5.0.0", "gatsby-source-prismic": "^5.3.1", - "gatsby-source-s3": "^3.2.3", "gatsby-transformer-sharp": "^5.7.0", "graphql": "^16.8.1", "graphql-http": "^1.18.0", @@ -70,6 +69,7 @@ "react-bootstrap-daterangepicker": "^7.0.0", "react-bootstrap-typeahead": "6.0.0-alpha.6", "react-d3-cloud": "^1.0.6", + "react-datetime": "^3.2.0", "react-dom": "^18.2.0", "react-feather": "^2.0.9", "react-helmet": "^6.1.0", @@ -88,7 +88,7 @@ "remark-gfm": "^3.0.1", "rich-text-diff": "^0.2.3", "sass": "^1.54.5", - "sharp": "^0.32.4", + "sharp": "^0.32.6", "slugify": "^1.6.5", "stemmer": "^1.0.5", "stopword": "^1.0.7", @@ -114,10 +114,13 @@ "test:e2e:ci": "start-server-and-test start http://localhost:8000 cypress:run" }, "devDependencies": { + "@babel/plugin-proposal-export-default-from": "^7.23.3", + "@cypress/code-coverage": "^3.12.15", "@netlify/plugin-gatsby": "^3.7.0", "@tailwindcss/typography": "^0.5.8", "autoprefixer": "^10.4.7", "babel-eslint": "^10.1.0", + "babel-plugin-istanbul": "^6.1.1", "cypress": "^13.0.0", "cypress-wait-for-stable-dom": "^0.1.0", "eslint": "^7.17.0", @@ -156,4 +159,4 @@ "minimist": "1.2.8", "d3-color": "3.1.0" } -} \ No newline at end of file +} diff --git a/site/gatsby-site/page-creators/createBackupsPage.js b/site/gatsby-site/page-creators/createBackupsPage.js index 018cba043b..255228aa59 100644 --- a/site/gatsby-site/page-creators/createBackupsPage.js +++ b/site/gatsby-site/page-creators/createBackupsPage.js @@ -1,43 +1,49 @@ const path = require('path'); -const createBackupsPage = (graphql, createPage) => { +const { S3Client, ListObjectsV2Command } = require('@aws-sdk/client-s3'); + +const config = require('../config'); + +const createBackupsPage = (_, createPage) => { return new Promise((resolve, reject) => { - resolve( - graphql( - ` - query AllS3Backups { - allS3Object { - nodes { - Size - LastModified - Key - } - } - } - ` - ).then((result) => { - if (result.errors) { - console.log(result.errors); // eslint-disable-line no-console - reject(result.errors); - } - const backups = result.data.allS3Object.nodes; + try { + const S3 = new S3Client({ + region: 'auto', + endpoint: `https://${config.cloudflareR2.accountId}.r2.cloudflarestorage.com`, + credentials: { + accessKeyId: config.cloudflareR2.accessKeyId, + secretAccessKey: config.cloudflareR2.secretAccessKey, + }, + }); - backups.sort(function (a, b) { - const bInt = Date.parse(b['LastModified']); + resolve( + S3.send(new ListObjectsV2Command({ Bucket: config.cloudflareR2.bucketName })).then( + (result) => { + const backups = result.Contents; - const aInt = Date.parse(a['LastModified']); + backups.sort(function (a, b) { + if (a.Key < b.Key) { + return 1; + } + if (a.Key > b.Key) { + return -1; + } + return 0; + }); - return bInt - aInt; - }); - createPage({ - path: '/research/snapshots', - component: path.resolve('./src/templates/backups.js'), - context: { - backups, - }, - }); - }) - ); + createPage({ + path: '/research/snapshots', + component: path.resolve('./src/templates/backups.js'), + context: { + backups, + }, + }); + } + ) + ); + } catch (error) { + reject(error); + } }); }; diff --git a/site/gatsby-site/src/api/parseNews.js b/site/gatsby-site/src/api/parseNews.js index 4c8b941f86..8e957d13a7 100644 --- a/site/gatsby-site/src/api/parseNews.js +++ b/site/gatsby-site/src/api/parseNews.js @@ -5,22 +5,26 @@ import axios from 'axios'; const stripImages = /!\[[^\]]*\]\((?.*?)(?="|\))(?".*")?\)/g; export default async function handler(req, res) { - const { url } = req.query; + try { + const { url } = req.query; - const article = await getArticle(url, { cookies: false }); + const article = await getArticle(url, { cookies: false }); - const response = { - title: article.title, - authors: article.author, - date_published: article.date_published - ? format(parseISO(article.date_published), 'yyyy-MM-dd') - : null, - date_downloaded: format(new Date(), 'yyyy-MM-dd'), - image_url: article.lead_image_url, - text: article.content?.replace(stripImages, '').trim(), - }; + const response = { + title: article.title, + authors: article.author, + date_published: article.date_published + ? format(parseISO(article.date_published), 'yyyy-MM-dd') + : null, + date_downloaded: format(new Date(), 'yyyy-MM-dd'), + image_url: article.lead_image_url, + text: article.content?.replace(stripImages, '').trim(), + }; - res.status(200).json(response); + res.status(200).json(response); + } catch (error) { + res.status(500).send([error.message, error.stack].filter((e) => e).join('\n\n')); + } } // Runs first with { cookies: false }, diff --git a/site/gatsby-site/src/api/riskManagement/v1/risks.js b/site/gatsby-site/src/api/riskManagement/v1/risks.js deleted file mode 100644 index dff30fd6a8..0000000000 --- a/site/gatsby-site/src/api/riskManagement/v1/risks.js +++ /dev/null @@ -1,209 +0,0 @@ -import { MongoClient } from 'mongodb'; - -import config from '../../../../config'; - -const handler = async (req, res) => { - - const mongoClient = new MongoClient( - config.mongodb.translationsConnectionString - ); - const db = mongoClient.db('aiidprod') - const incidentsCollection = db.collection('incidents'); - const classificationsCollection = db.collection('classifications'); - - const classificationsMatchingSearchTags = ( - await classificationsCollection.find( - getRiskClassificationsMongoQuery(req.query), - ).toArray() - ); - - const tagsByIncidentId = {}; - for (const classification of classificationsMatchingSearchTags) { - for (const id of classification.incidents) { - tagsByIncidentId[id] = ( - (tagsByIncidentId[id] || []).concat( - tagsFromClassification(classification) - ) - ); - } - } - - const incidentIdsMatchingSearchTags = ( - classificationsMatchingSearchTags.map(c => c.incidents).flat() - ); - - const incidentsMatchingSearchTags = await incidentsCollection.find( - { incident_id: { $in: incidentIdsMatchingSearchTags } }, - { projection: { incident_id: 1, title: 1, description: 1 }} - ).toArray(); - - const failureAttributeQuery = { - attributes: { - $elemMatch: { - short_name: { - $in: [ - "Known AI Technical Failure", - "Potential AI Technical Failure" - ] - } - } - } - }; - const failureClassificationsMatchingIncidentIds = ( - await classificationsCollection.find( - { - incidents: { - $elemMatch: { - $in: incidentIdsMatchingSearchTags - } - }, - ...failureAttributeQuery - }, - { - projection: { - namespace: 1, - incidents: 1, - ...failureAttributeQuery - } - } - ).toArray() - ); - - const matchingClassificationsByFailure = ( - groupable(failureClassificationsMatchingIncidentIds).groupByMultiple( - classification => tagsFromClassification(classification) - ) - ); - - const risks = Object.keys(matchingClassificationsByFailure).map( - failure => ({ - api_message: "This is an experimental an unsupported API", - tag: failure, - precedents: matchingClassificationsByFailure[failure].map( - failureClassification => { - const incidents = incidentsMatchingSearchTags.filter( - incident => failureClassification.incidents.includes(incident.incident_id) - ); - return incidents.map(incident => ({ - incident_id: incident?.incident_id, - title: incident?.title, - description: incident?.description, - tags: tagsByIncidentId[incident?.incident_id] - })); - } - ).flat() - }) - ).sort((a, b) => b.precedents.length - a.precedents.length); - - res.status(200).json(risks); - -} - -const getRiskClassificationsMongoQuery = (queryParams) => { - const tagStrings = queryParams.tags.split('___'); - - const tagSearch = {}; - - for (const tagString of tagStrings) { - const parts = tagString.split(":"); - const namespace = parts[0]; - if (!tagSearch[namespace]) { - tagSearch[namespace] = []; - } - const tag = {}; - tag.short_name = parts[1]; - if (parts.length > 2) { - tag.value_json = {$regex: `"${parts[2]}"`}; - } - tagSearch[namespace].push(tag); - } - - return { - $or: Object.keys(tagSearch).map( - namespace => ({ - namespace, - attributes: { - $elemMatch: { - $or: tagSearch[namespace] - } - } - }) - ) - } -} - -const tagsFromClassification = (classification) => ( - // classification: - // { - // attributes: [ - // { short_name: "Known AI Goal"}, - // value_json: '["Content Recommendation", "Something"]' } - // ... - // ] - // } - joinArrays( - classification.attributes.filter(a => ![null, undefined].includes(a.value_json)).map( - attribute => ( - [].concat(parseJson(attribute.value_json)) - .filter(value => Array.isArray(value) || typeof value !== 'object') - .map( - value => [ - classification.namespace, - attribute.short_name, - value - ].join(':') - ) - ) - ) - ) -); - -const parseJson = (json) => { - try { - return JSON.parse(json); - } catch (e) { - throw new Error('Could not parse ' + json) - } -} - -const joinArrays = (arrays) => arrays.reduce( - (result, array) => result.concat(array), [] -); - -const groupable = (array) => { - array.groupBy = (keyFunction, valueFunction) => { - const groups = {}; - for (const element of array) { - const key = keyFunction(element); - if (!groups[key]) { - groups[key] = []; - } - groups[key].push( - valueFunction ? valueFunction(element) : element - ); - } - return groups; - } - array.groupByMultiple = (keyFunction, valueFunction) => { - const groups = {}; - for (const element of array) { - const keys = keyFunction(element); - for (const key of keys) { - if (!groups[key]) { - groups[key] = new Set(); - } - groups[key].add( - valueFunction ? valueFunction(element) : element - ); - } - } - for (const group in groups) { - groups[group] = Array.from(groups[group]); - } - return groups; - } - return array; -} - -export default handler; - diff --git a/site/gatsby-site/src/components/blog/PostsListing.js b/site/gatsby-site/src/components/blog/PostsListing.js index bdd07bfc37..fe272848d1 100644 --- a/site/gatsby-site/src/components/blog/PostsListing.js +++ b/site/gatsby-site/src/components/blog/PostsListing.js @@ -3,15 +3,33 @@ import PostPreview from './PostPreview'; import PrismicPostPreview from './PrismicPostPreview'; export default function PostsListing({ posts, mdxBlogPosts }) { + // Add an mdx field to each object and flatten the structure + const mdxBlogPostsWithFlag = mdxBlogPosts.map((post) => ({ + ...post, + date: post.frontmatter.date, + mdx: true, + })); + + const postsWithFlag = posts.map((post) => ({ + ...post, + date: post.node.data.date, + mdx: false, + })); + + const mergedPosts = [...mdxBlogPostsWithFlag, ...postsWithFlag]; + + const sortedPosts = mergedPosts.sort((a, b) => new Date(b.date) - new Date(a.date)); + return ( <>
- {posts.map((p) => ( - - ))} - {mdxBlogPosts.map((p) => ( - - ))} + {sortedPosts.map((p) => { + return p.mdx ? ( + + ) : ( + + ); + })}
); diff --git a/site/gatsby-site/src/components/blog/PrismicBlogPost.js b/site/gatsby-site/src/components/blog/PrismicBlogPost.js index f1be929965..8d1804a9f4 100644 --- a/site/gatsby-site/src/components/blog/PrismicBlogPost.js +++ b/site/gatsby-site/src/components/blog/PrismicBlogPost.js @@ -12,7 +12,7 @@ import Outline from 'components/Outline'; import AiidHelmet from 'components/AiidHelmet'; const PrismicBlogPost = ({ post, location }) => { - const metaTitle = post.data.metaTitle; + const metaTitle = post.data.metatitle; const metaDescription = post.data.metaDescription; diff --git a/site/gatsby-site/src/components/checklists/CheckListForm.js b/site/gatsby-site/src/components/checklists/CheckListForm.js index 8fb362a9fc..efd2fb752e 100644 --- a/site/gatsby-site/src/components/checklists/CheckListForm.js +++ b/site/gatsby-site/src/components/checklists/CheckListForm.js @@ -2,9 +2,20 @@ import React, { useEffect, useState, useRef } from 'react'; import { Form } from 'formik'; import { Button, Textarea, Spinner } from 'flowbite-react'; import { Trans, useTranslation } from 'react-i18next'; -import { useMutation } from '@apollo/client'; +import { useMutation, useApolloClient, gql } from '@apollo/client'; import { LocalizedLink } from 'plugins/gatsby-theme-i18n'; import debounce from 'lodash/debounce'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faEnvelope, + faPlusCircle, + faWindowMaximize, + faWindowMinimize, + faBullseye, + faMicrochip, + faArrowsTurnToDots, + faTag, +} from '@fortawesome/free-solid-svg-icons'; import { DELETE_CHECKLIST } from '../../graphql/checklists'; import { @@ -36,7 +47,7 @@ export default function CheckListForm({ const userIsOwner = values.owner_id == user.id; - const owner = users.find((u) => (u.userId = values.owner_id)); + const owner = users.find((u) => u.userId == values.owner_id); const [deleteChecklist] = useMutation(DELETE_CHECKLIST); @@ -69,8 +80,18 @@ export default function CheckListForm({ ...(values['tags_other'] || []), ]; + const apolloClient = useApolloClient(); + useEffect(() => { - searchRisks({ values, setFieldValue, setRisksLoading, setAllPrecedents, addToast, t }); + searchRisks({ + values, + setFieldValue, + setRisksLoading, + setAllPrecedents, + addToast, + t, + apolloClient, + }); }, [values['tags_goals'], values['tags_methods'], values['tags_other']]); useEffect(() => { @@ -134,6 +155,7 @@ export default function CheckListForm({ {userIsOwner && ( @@ -145,6 +167,7 @@ export default function CheckListForm({ This feature is in development. Data entered will not be retained. + Checklists are not private data. They will appear in public database snapshots.
@@ -166,6 +189,7 @@ export default function CheckListForm({ ) } > + Expand all {userIsOwner && ( @@ -188,6 +213,7 @@ export default function CheckListForm({ ); }} > + Add Risk )} @@ -225,6 +251,7 @@ const AboutSystem = ({ formAbout, debouncedSetFieldValue, userIsOwner }) => { return (