diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..610dee0 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": ["@babel/preset-env"], + "plugins": [ + "@babel/plugin-transform-runtime", + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-transform-react-jsx", + "@babel/plugin-syntax-function-bind", + "@babel/plugin-proposal-function-bind", + ["@babel/plugin-proposal-class-properties", {loose: true}], + ] +} diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..d1a0d79 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,16 @@ +[run] +source = ./src/newsreader/ +omit = + **/tests/** + **/migrations/** + **/conf/** + **/apps.py + **/admin.py + **/tests.py + **/urls.py + **/wsgi.py + **/celery.py + **/__init__.py + +[html] +directory = coverage diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 5257a12..0000000 --- a/.editorconfig +++ /dev/null @@ -1,25 +0,0 @@ -# https://editorconfig.org - -# top-most EditorConfig file -root = true - -# Unix-style newlines with a newline ending every file -[*] -end_of_line = lf -trim_trailing_whitespace = true - -[*.py] -indent_style = space -indent_size = 4 - -[*.{yaml,yml,toml,md}] -indent_style = space -indent_size = 2 - -[Dockerfile*] -indent_style = space -indent_size = 4 - -[*.json] -indent_style = space -indent_size = 2 diff --git a/.gitignore b/.gitignore index 3546fd2..ef20aea 100644 --- a/.gitignore +++ b/.gitignore @@ -115,7 +115,7 @@ celerybeat-schedule *.sage.py # Environments -*.env +.env .venv env/ venv/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..beb864f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,30 @@ +stages: + - build + - test + - lint + - release + - deploy + +variables: + PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" + DJANGO_SETTINGS_MODULE: "newsreader.conf.gitlab" + POSTGRES_HOST: "$POSTGRES_HOST" + POSTGRES_DB: "$POSTGRES_NAME" + POSTGRES_NAME: "$POSTGRES_NAME" + POSTGRES_USER: "$POSTGRES_USER" + POSTGRES_PASSWORD: "$POSTGRES_PASSWORD" + +cache: + key: "$CI_COMMIT_REF_SLUG" + paths: + - .venv/ + - .cache/pip + - .cache/poetry + - node_modules/ + +include: + - local: '/gitlab-ci/build.yml' + - local: '/gitlab-ci/test.yml' + - local: '/gitlab-ci/lint.yml' + - local: '/gitlab-ci/release.yml' + - local: '/gitlab-ci/deploy.yml' diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..0c8e37f --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,12 @@ +[settings] +include_trailing_comma = true +line_length = 88 +multi_line_output = 3 +skip = env/, venv/ +default_section = THIRDPARTY +known_first_party = newsreader +known_django = django +sections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +lines_between_types=1 +lines_after_imports=2 +lines_between_types=1 diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index b009dfb..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -lts/* diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..146a217 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 90, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true, + "arrowParens": "avoid" +} diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml deleted file mode 100644 index 519e330..0000000 --- a/.woodpecker/build.yaml +++ /dev/null @@ -1,10 +0,0 @@ -when: - - event: push - - event: pull_request - - event: manual - -steps: - - image: node:lts-alpine - commands: - - npm install - - npm run build:prod diff --git a/.woodpecker/lint.yaml b/.woodpecker/lint.yaml deleted file mode 100644 index 64ee04b..0000000 --- a/.woodpecker/lint.yaml +++ /dev/null @@ -1,18 +0,0 @@ -when: - - event: push - - event: pull_request - - event: manual - -steps: - - name: python linting - image: ghcr.io/astral-sh/uv:python3.11-alpine - commands: - - uv sync --group ci - - uv run --no-sync -- ruff check src/ - - uv run --no-sync -- ruff format --check src/ - - - name: javascript linting - image: node:lts-alpine - commands: - - npm ci - - npm run lint diff --git a/.woodpecker/tests.yaml b/.woodpecker/tests.yaml deleted file mode 100644 index 95092f6..0000000 --- a/.woodpecker/tests.yaml +++ /dev/null @@ -1,37 +0,0 @@ -when: - - event: push - - event: pull_request - - event: manual - -services: - - name: postgres - image: postgres:15 - environment: - POSTGRES_NAME: &db-name newsreader - POSTGRES_USER: &db-user newsreader - POSTGRES_PASSWORD: &db-password sekrit - - name: memcached - image: memcached:1.5.22 - -steps: - - name: python tests - image: ghcr.io/astral-sh/uv:python3.11-alpine - environment: - DJANGO_SETTINGS_MODULE: "newsreader.conf.ci" - DJANGO_SECRET_KEY: sekrit - POSTGRES_HOST: postgres - POSTGRES_PORT: 5432 - POSTGRES_DB: *db-name - POSTGRES_USER: *db-user - POSTGRES_PASSWORD: *db-password - commands: - - pip install uv - - uv sync --group ci - - uv run --no-sync -- coverage run ./src/manage.py test newsreader - - uv run --no-sync -- coverage report --show-missing - - - name: javascript tests - image: node:lts-alpine - commands: - - npm ci - - npm test diff --git a/CHANGELOG.md b/CHANGELOG.md index b1341d4..9c20312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,66 +1,5 @@ # Changelog -## 0.5.3 - -- Apply query optimizations for retrieving posts - -## 0.5.2 - -- Add missing `VERSION` environment variable - -## 0.5.1 - -- Use line-through styling for read posts -- Use full height for post layout - -## 0.5.0 - -- Upgrade python to 3.11 -- Upgrade django to 4.2 -- Migrate from pip-tools to uv -- Migrate from black to ruff for formatting -- Upgrade webpack to 5.9 (with various tooling) -- Styling refactor -- Mobile/tablet layout added - -## 0.4.4 - -- Sort posts before storing in redux store - -## 0.4.3 - -- Use `IntersectionObserver` to paginate - -## 0.4.2 - -- Set `SECURE_PROXY_SSL_HEADER` setting for production - -## 0.4.1 - -- Add missing env variables - -## 0.4.0 - -- Add Makefile & use `pip-tools` to generate dependencies -- Add `pyproject.toml` -- Update dependencies -- Update docker-compose setup -- Default to `newsreader.conf.docker` settings module -- Add scroll to top/bottom buttons - -## 0.3.13.8 - -- Update dependencies -- Fix csrf_token's not rendering - -## 0.3.13.7 - -- Check for Twitter error codes in response - -## 0.3.13.6 - -- Try to load sentry by default for all environments - ## 0.3.13.5 - Set response keyword argument diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 0ffa683..0000000 --- a/Dockerfile +++ /dev/null @@ -1,84 +0,0 @@ -# stage 1 -FROM python:3.11-alpine AS backend - -ARG USER_ID=1000 -ARG GROUP_ID=1000 -ARG UV_LINK_MODE=copy - -RUN apk update \ - && apk add --no-cache \ - vim \ - curl \ - gettext - -RUN addgroup -g $USER_ID newsreader && adduser -Du $GROUP_ID -G newsreader newsreader - -RUN mkdir --parents /app/src /app/logs /app/media /app/bin /app/static \ - && chown -R newsreader:newsreader /app - -WORKDIR /app - -USER newsreader - -COPY --chown=newsreader:newsreader uv.lock pyproject.toml /app/ - -COPY --from=ghcr.io/astral-sh/uv:python3.11-alpine /usr/local/bin/uv /bin/uv - -RUN --mount=type=cache,uid=$USER_ID,gid=$GROUP_ID,target=/home/newsreader/.cache/uv \ - uv sync --frozen --no-default-groups --no-install-project - -COPY --chown=newsreader:newsreader ./bin/docker-entrypoint.sh /app/bin/docker-entrypoint.sh - -VOLUME ["/app/logs", "/app/media", "/app/static"] - - - -# stage 2 -FROM node:lts-alpine AS frontend-build - -ARG BUILD_ARG=prod - -WORKDIR /app - -RUN chown node:node /app - -USER node - -COPY --chown=node:node ./package*.json ./webpack.*.js ./babel.config.js /app/ - -RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \ - npm ci - -COPY --chown=node:node ./src /app/src - -RUN npm run build:$BUILD_ARG - - - -# stage 3 -FROM backend AS production - -COPY --from=frontend-build --chown=newsreader:newsreader \ - /app/src/newsreader/static /app/src/newsreader/static - -RUN --mount=type=cache,uid=$USER_ID,gid=$GROUP_ID,target=/home/newsreader/.cache/uv \ - uv sync --frozen --only-group production --extra sentry - -COPY --chown=newsreader:newsreader ./src /app/src - -ENV DJANGO_SETTINGS_MODULE=newsreader.conf.production - -# Note that the static volume will have to be recreated to be pre-populated -# correctly with the latest static files. See -# https://docs.docker.com/storage/volumes/#populate-a-volume-using-a-container -RUN uv run --no-sync -- src/manage.py collectstatic --noinput - - - -# (optional) stage 4 -FROM backend AS development - -RUN --mount=type=cache,uid=$USER_ID,gid=$GROUP_ID,target=/home/newsreader/.cache/uv \ - uv sync --frozen --group development - -ENV DJANGO_SETTINGS_MODULE=newsreader.conf.docker diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index ed19be2..0000000 --- a/babel.config.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = api => { - const isTest = api.env('test'); - - const preset = [ - "@babel/preset-env", { targets: 'defaults' } - ]; - const testPreset = [ - "@babel/preset-env", { targets: { node: process.versions.node } } - ]; - - const plugins = [ - "@babel/plugin-syntax-dynamic-import", - "@babel/plugin-transform-react-jsx", - "@babel/plugin-proposal-class-properties" - ] - - return { - "presets": [isTest ? testPreset : preset], - "plugins": plugins - } -} diff --git a/bin/docker-entrypoint.sh b/bin/docker-entrypoint.sh deleted file mode 100755 index bb473e6..0000000 --- a/bin/docker-entrypoint.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -uv run --no-sync -- /app/src/manage.py migrate - -exec "$@" diff --git a/config/nginx/conf.d/local.conf b/config/nginx/conf.d/local.conf deleted file mode 100644 index b63a1ef..0000000 --- a/config/nginx/conf.d/local.conf +++ /dev/null @@ -1,21 +0,0 @@ -upstream gunicorn { - server django:8000; -} - -server { - listen 80; - server_name localhost; - - access_log /var/log/nginx/access_log; - error_log /var/log/nginx/error_log; - - location /static/ { - root /app; - } - - location / { - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $host; - proxy_pass http://gunicorn; - } -} diff --git a/docker-compose.development.yml b/docker-compose.development.yml deleted file mode 100644 index 9045200..0000000 --- a/docker-compose.development.yml +++ /dev/null @@ -1,36 +0,0 @@ -volumes: - static-files: - -services: - django: - build: &app-development-build - target: development - command: uv run --no-sync -- /app/src/manage.py runserver 0.0.0.0:8000 - environment: &django-env - DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE:-newsreader.conf.docker} - ports: - - "${DJANGO_PORT:-8000}:8000" - volumes: - - ./src:/app/src - - static-files:/app/src/newsreader/static - stdin_open: true - tty: true - - celery: - build: - <<: *app-development-build - environment: - <<: *django-env - volumes: - - ./src/:/app/src - - webpack: - build: - target: frontend-build - context: . - args: - BUILD_ARG: "dev" - command: npm run build:watch - volumes: - - ./src/:/app/src - - static-files:/app/src/newsreader/static diff --git a/docker-compose.production.yml b/docker-compose.production.yml deleted file mode 100644 index 24c8cd1..0000000 --- a/docker-compose.production.yml +++ /dev/null @@ -1,16 +0,0 @@ -volumes: - logs: - static-files: - -services: - nginx: - image: nginx:1.23 - depends_on: - django: - condition: service_healthy - ports: - - "${NGINX_HTTP_PORT:-80}:80" - volumes: - - ./config/nginx/conf.d:/etc/nginx/conf.d - - logs:/var/log/nginx - - static-files:/app/static diff --git a/docker-compose.yml b/docker-compose.yml index c348d96..8ce24e3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,126 +1,62 @@ +version: "3" volumes: - logs: - media: postgres-data: static-files: - -x-db-connection-env: &db-connection-env - POSTGRES_HOST: ${POSTGRES_HOST:-db} - POSTGRES_PORT: ${POSTGRES_PORT:-5432} - POSTGRES_DB: &pg-database ${POSTGRES_DB:-newsreader} - POSTGRES_USER: &pg-user ${POSTGRES_USER:-newsreader} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-newsreader} - -x-db-env: &db-env - <<: *db-connection-env - PGUSER: *pg-user - PGDATABASE: *pg-database - -x-django-env: &django-env - <<: *db-connection-env - - ALLOWED_HOSTS: ${ALLOWED_HOSTS:-localhost,127.0.0.1,django} - INTERNAL_IPS: ${INTERNAL_IPS:-localhost,127.0.0.1,django} - - # see token_urlsafe from python's secret module to generate one - DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-Ojg68lYsP3kq2r5JgozUzKVSRFywm17BTMS5iwpLM44} - DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE:-newsreader.conf.production} - - ADMINS: ${ADMINS:-""} - - VERSION: ${VERSION:-""} - - # Email - EMAIL_HOST: ${EMAIL_HOST:-localhost} - EMAIL_PORT: ${EMAIL_PORT:-25} - EMAIL_HOST_USER: ${EMAIL_HOST_USER:-""} - EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD:-""} - EMAIL_USE_TLS: ${EMAIL_USE_TLS:-no} - EMAIL_USE_SSL: ${EMAIL_USE_SSL:-no} - EMAIL_DEFAULT_FROM: ${EMAIL_DEFAULT_FROM:-webmaster@localhost} - - # Sentry - SENTRY_DSN: ${SENTRY_DSN:-""} + node-modules: services: db: + image: postgres environment: - <<: *db-env - image: postgres:15 - healthcheck: - test: /usr/bin/pg_isready - start_period: 10s - interval: 5s - timeout: 10s - retries: 10 + POSTGRES_DB: "newsreader" + POSTGRES_USER: "newsreader" + POSTGRES_PASSWORD: "newsreader" volumes: - postgres-data:/var/lib/postgresql/data - rabbitmq: - image: rabbitmq:4 - + image: rabbitmq:3.7 memcached: image: memcached:1.6 + ports: + - "11211:11211" entrypoint: - memcached - -m 64 - - django: - build: &app-build - context: . - target: production - environment: - <<: *django-env - entrypoint: ["/bin/sh", "/app/bin/docker-entrypoint.sh"] - command: | - uv run --no-sync -- - gunicorn - --bind 0.0.0.0:8000 - --workers 3 - --chdir /app/src/ - newsreader.wsgi:application - healthcheck: - test: /usr/bin/curl --fail http://django:8000 || exit 1 - start_period: 10s - interval: 10s - timeout: 10s - retries: 5 - depends_on: - memcached: - condition: service_started - db: - condition: service_healthy - volumes: - - logs:/app/logs - - media:/app/media - - static-files:/app/static - celery: build: - <<: *app-build + context: . + dockerfile: ./docker/django + command: celery worker -n worker1@%h -n worker2@%h --app newsreader --loglevel INFO --concurrency 2 --workdir /app/src/ --beat --scheduler django environment: - <<: *django-env - command: | - uv run --no-sync -- - celery - --app newsreader - --workdir /app/src/ - worker --loglevel INFO - --concurrency 2 - --beat - --scheduler django - -n worker1@%h - -n worker2@%h - healthcheck: - test: uv run --no-sync -- celery --app newsreader status || exit 1 - start_period: 10s - interval: 10s - timeout: 10s - retries: 5 + - DJANGO_SETTINGS_MODULE=newsreader.conf.docker depends_on: - rabbitmq: - condition: service_started - django: - condition: service_healthy + - rabbitmq + - memcached volumes: - - logs:/app/logs + - .:/app + django: + build: + context: . + dockerfile: ./docker/django + command: python /app/src/manage.py runserver 0.0.0.0:8000 + environment: + - DJANGO_SETTINGS_MODULE=newsreader.conf.docker + ports: + - "8000:8000" + depends_on: + - db + - memcached + volumes: + - .:/app + - static-files:/app/src/newsreader/static + stdin_open: true + tty: true + webpack: + build: + context: . + dockerfile: ./docker/webpack + command: npm run build:watch + volumes: + - .:/app + - static-files:/app/src/newsreader/static + - node-modules:/app/node_modules diff --git a/docker/django b/docker/django new file mode 100644 index 0000000..871828a --- /dev/null +++ b/docker/django @@ -0,0 +1,10 @@ +FROM python:3.7-buster + +RUN pip install poetry + +WORKDIR /app +COPY poetry.lock pyproject.toml /app/ + +RUN poetry config virtualenvs.create false && poetry install --no-interaction + +COPY . /app/ diff --git a/docker/webpack b/docker/webpack new file mode 100644 index 0000000..6909ee9 --- /dev/null +++ b/docker/webpack @@ -0,0 +1,9 @@ +FROM node:12 + +WORKDIR /app + +COPY package.json package-lock.json /app/ + +RUN npm install + +COPY . /app/ diff --git a/gitlab-ci/build.yml b/gitlab-ci/build.yml new file mode 100644 index 0000000..c8df615 --- /dev/null +++ b/gitlab-ci/build.yml @@ -0,0 +1,7 @@ +static: + stage: build + image: node:12 + before_script: + - npm install + script: + - npm run build diff --git a/gitlab-ci/deploy.yml b/gitlab-ci/deploy.yml new file mode 100644 index 0000000..ba0ba46 --- /dev/null +++ b/gitlab-ci/deploy.yml @@ -0,0 +1,22 @@ +deploy: + stage: deploy + image: python:3.7 + environment: + name: production + url: rss.fudiggity.nl + rules: + - if: $CI_COMMIT_TAG + before_script: + - pip install ansible --quiet + - git clone https://git.fudiggity.nl/ansible/newsreader.git deployment --branch master + - cd deployment + - ansible-galaxy install -r requirements.yml + - mkdir /root/.ssh && echo "$DEPLOY_HOST_KEY" > /root/.ssh/known_hosts + - echo "$DEPLOY_KEY" > deploy_key && chmod 0600 deploy_key + - echo "$VAULT_PASSWORD" > vault + script: + - > + ansible-playbook playbook.yml + --private-key deploy_key + --vault-password-file vault + --extra-vars "app_branch=$CI_COMMIT_TAG" diff --git a/gitlab-ci/lint.yml b/gitlab-ci/lint.yml new file mode 100644 index 0000000..0300c33 --- /dev/null +++ b/gitlab-ci/lint.yml @@ -0,0 +1,28 @@ +python-linting: + stage: lint + image: python:3.7 + before_script: + - pip install poetry --quiet + - poetry config cache-dir ~/.cache/poetry + - poetry config virtualenvs.in-project true + - poetry install --no-interaction --quiet + script: + - poetry run isort src/ --check-only --recursive + - poetry run black src/ --line-length 88 --check + - poetry run autoflake src/ --check --recursive --remove-all-unused-imports --ignore-init-module-imports + only: + refs: + - development + - merge_requests + +javascript-linting: + stage: lint + image: node:12 + before_script: + - npm install + script: + - npm run lint + only: + refs: + - development + - merge_requests diff --git a/gitlab-ci/release.yml b/gitlab-ci/release.yml new file mode 100644 index 0000000..571be3c --- /dev/null +++ b/gitlab-ci/release.yml @@ -0,0 +1,12 @@ +release: + stage: release + image: registry.gitlab.com/gitlab-org/release-cli:latest + rules: + - if: $CI_COMMIT_TAG + script: + - echo 'running release job' + release: + name: 'Release $CI_COMMIT_TAG' + description: './CHANGELOG.md' + tag_name: '$CI_COMMIT_TAG' + ref: '$CI_COMMIT_TAG' diff --git a/gitlab-ci/test.yml b/gitlab-ci/test.yml new file mode 100644 index 0000000..3114a87 --- /dev/null +++ b/gitlab-ci/test.yml @@ -0,0 +1,23 @@ +python-tests: + stage: test + coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' + services: + - postgres:11 + - memcached:1.5.22 + image: python:3.7 + before_script: + - pip install poetry --quiet + - poetry config cache-dir .cache/poetry + - poetry config virtualenvs.in-project true + - poetry install --no-interaction --quiet + script: + - poetry run coverage run src/manage.py test newsreader + - poetry run coverage report + +javascript-tests: + stage: test + image: node:12 + before_script: + - npm install + script: + - npm test diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..01afeb8 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,188 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // Respect "browser" field in package.json when resolving modules + // browser: false, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: null, + + // The directory where Jest should output its coverage files + coverageDirectory: 'coverage', + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: null, + + // A path to a custom dependency extractor + // dependencyExtractor: null, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: null, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: null, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: null, + + // Run tests from one or more projects + // projects: null, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: null, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + rootDir: 'src/newsreader/js/tests/', + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: 'node', + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: null, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + // transform: null, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: null, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/package-lock.json b/package-lock.json index 59e4d2b..581f084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4689 +1,5367 @@ { "name": "newsreader", - "version": "0.5.3", - "lockfileVersion": 3, + "version": "0.1.0", + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "newsreader", - "version": "0.5.3", - "license": "GPL-3.0-or-later", - "dependencies": { - "@fortawesome/fontawesome-free": "^5.15.2", - "js-cookie": "^2.2.1", - "lodash": "^4.17.20", - "object-assign": "^4.1.1", - "react-redux": "^7.2.2", - "redux": "^4.0.5", - "redux-logger": "^3.0.6", - "redux-thunk": "^2.3.0" - }, - "devDependencies": { - "@babel/core": "^7.12.13", - "@babel/plugin-proposal-class-properties": "^7.12.13", - "@babel/plugin-proposal-function-bind": "^7.12.13", - "@babel/plugin-transform-react-jsx": "^7.12.13", - "@babel/preset-env": "^7.12.13", - "@babel/register": "^7.12.13", - "@babel/runtime": "^7.12.13", - "babel-jest": "^29.7.0", - "babel-loader": "^8.2.2", - "clean-webpack-plugin": "^3.0.0", - "css-loader": "^7.1.2", - "fetch-mock": "^8.3.2", - "jest": "^29.7.0", - "mini-css-extract-plugin": "^2.9.1", - "node-fetch": "^2.6.1", - "prettier": "^1.19.1", - "react": "^16.14.0", - "react-dom": "^16.14.0", - "redux-mock-store": "^1.5.4", - "sass": "^1.52.1", - "sass-loader": "^8.0.2", - "style-loader": "^2.0.0", - "url-loader": "^4.1.1", - "webpack": "^5.94.0", - "webpack-cli": "^5.1.4", - "webpack-merge": "^4.2.2" + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } + "@babel/compat-data": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.13.tgz", + "integrity": "sha512-U/hshG5R+SIoW7HVWIdmy1cB7s3ki+r3FpyEZiCgpi4tFgPnX/vynY80ZGSASOIrUM6O7VxOgCZgdt7h97bUGg==", + "dev": true }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "@babel/core": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz", + "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", - "convert-source-map": "^2.0.0", + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helpers": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", + "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "^5.4.1", + "source-map": "^0.5.0" }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.25.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", - "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "@babel/generator": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz", + "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/types": "^7.7.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", + "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", + "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.12.13", + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "@babel/helper-compilation-targets": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.13.tgz", + "integrity": "sha512-dXof20y/6wB5HnLOGyLh/gobsMvDNoekcC+8MCV2iaTd5JemhFkPD73QB+tK3iFC9P0xJC73B6MvKkyUfS9cCw==", "dev": true, - "license": "MIT", + "requires": { + "@babel/compat-data": "^7.12.13", + "@babel/helper-validator-option": "^7.12.11", + "browserslist": "^4.14.5", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.13.tgz", + "integrity": "sha512-Vs/e9wv7rakKYeywsmEBSRC9KtmE7Px+YBlESekLeJOF0zbGUicGfXSNi3o+tfXSNS48U/7K9mIOOCR79Cl3+Q==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13" + }, "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", - "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", + "@babel/helper-create-regexp-features-plugin": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.13.tgz", + "integrity": "sha512-XC+kiA0J3at6E85dL5UnCYfVOcIZ834QcAY0TIpgUVnz0zDzg+0TtvZTnJ4g9L1dPRGe30Qi03XCIS4tYCLtqw==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.13.tgz", + "integrity": "sha512-5loeRNvMo9mx1dA/d6yNi+YiKziJZFylZnCo1nmFF4qPU4yJ14abhWESuSMQSlQxWdxdOFzxXjk/PpfudTtYyw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.4", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", - "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", + "@babel/helper-function-name": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", + "dev": true, + "requires": { + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.12.13.tgz", + "integrity": "sha512-KSC5XSj5HreRhYQtZ3cnSnQwDzgnbdUDEFsxkN0m6Q3WrCRt72xrnZ8+h+pX7YxM7hr87zIO3a/v5p/H3TrnVw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "@babel/helper-member-expression-to-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz", + "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", - "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", + "@babel/helper-module-imports": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, - "license": "MIT", + "requires": { + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "@babel/helper-module-transforms": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", + "lodash": "^4.17.19" + }, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.13.tgz", + "integrity": "sha512-Qa6PU9vNcj1NZacZZI1Mvwt+gXDH6CTfgAkSjeRMLE8HxtDK76+YDId6NQR+z7Rgd5arhD2cIbS74r0SxD6PDA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-wrap-function": "^7.12.13", + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", - "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-wrap-function": "^7.25.0", - "@babel/traverse": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", - "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", + "@babel/helper-simple-access": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/traverse": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/types": "^7.12.1" + }, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", + "@babel/helper-split-export-declaration": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", "dev": true, - "license": "MIT", + "requires": { + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz", + "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.13.tgz", + "integrity": "sha512-t0aZFEmBJ1LojdtJnhOaQEVejnzYhyjWHSsNSNo8vOYRbAJNh6r6GQF7pd36SqG7OKGbn+AewVQ/0IfYfIuGdw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/helpers": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz", + "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==", + "dev": true, + "requires": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", - "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, - "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, - "license": "MIT", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.7.tgz", + "integrity": "sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.13.tgz", + "integrity": "sha512-1KH46Hx4WqP77f978+5Ye/VUbuwQld2hph70yaw2hXS2v7ER2f3nlpNMu909HO2rbvP0NKLlMVDPh9KXklVMhA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-remap-async-to-generator": "^7.12.13", + "@babel/plugin-syntax-async-generators": "^7.8.0" + }, "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "@babel/plugin-proposal-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.13.tgz", + "integrity": "sha512-8SCJ0Ddrpwv4T7Gwb33EmW1V9PY5lggTO+A8WjyIwxrSHDUyBw4MtF96ifn1n8H806YlxbVCoKXbbmzD6RD+cA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "@babel/plugin-proposal-dynamic-import": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", + "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + }, "dependencies": { - "@babel/types": "^7.25.4" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", - "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", - "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "@babel/plugin-proposal-function-bind": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.12.13.tgz", + "integrity": "sha512-HdFUUOUhB5WuNug+rfhcRvjqjjtKdJlWr6kgIezpbh9xiIEza/pPWw+bJeH2GdGeUyNqhRIYeFKt0M3/xXWp1w==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-function-bind": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", - "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", + "@babel/plugin-proposal-json-strings": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.13.tgz", + "integrity": "sha512-v9eEi4GiORDg8x+Dmi5r8ibOe0VXoKDeNPYcTTxdGN4eOWikrJfDJCJrr1l5gKGvsNyGJbrfMftC2dTL6oz7pg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-json-strings": "^7.8.0" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.13.tgz", + "integrity": "sha512-fqmiD3Lz7jVdK6kabeSr1PZlWSUVqSitmHEe3Z00dtGTKieWnX9beafvavc32kjORa5Bai4QNHgFDwWJP+WtSQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", - "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.13.tgz", + "integrity": "sha512-Qoxpy+OxhDBI5kRqliJFAl4uWXk3Bn24WeFstPH0iLymFehSAUR8MHpqU7njyXv/qbo7oN6yTy5bfCmXdKpo1Q==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "@babel/plugin-proposal-numeric-separator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", + "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-proposal-function-bind": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.24.7.tgz", - "integrity": "sha512-cq2mwxcvNAWWL+IiqiSiVhCeqTQs532Ktl3N2FMuW0bQVF/N0W6QNyywO+KkM3Yr/jwYmjeSS+yKQQUh79VOxQ==", + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.13.tgz", + "integrity": "sha512-WvA1okB/0OS/N3Ldb3sziSrXg6sRphsBgqiccfcQq7woEn5wQLNX82Oc4PlaFcdwcWHuQXAtb8ftbS8Fbsg/sg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-function-bind": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + } } }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.13.tgz", + "integrity": "sha512-9+MIm6msl9sHWg58NvqpNpLtuFbmpFYk37x8kgnGzAHvX35E1FyAwSUt5hIkSoWJFSAH+iwU8bJ4fcD1zKXOzg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-async-generators": { + "@babel/plugin-proposal-optional-chaining": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.13.tgz", + "integrity": "sha512-0ZwjGfTcnZqyV3y9DSD1Yk3ebp+sIUpT2YDqP8hovzaNZnQq2Kd7PEqa6iOIUDBXBt7Jl3P7YAcEIL5Pz8u09Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.13.tgz", + "integrity": "sha512-sV0V57uUwpauixvR7s2o75LmwJI6JECwm5oPUY5beZB1nBl2i37hc7CJGqB5G+58fur5Y6ugvl3LRONk5x34rg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", + "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } + } + }, + "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-class-properties": { + "@babel/plugin-syntax-class-properties": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.12.13" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { + "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { + "@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.8.3" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-function-bind": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.24.7.tgz", - "integrity": "sha512-dqm7VhgJ2sXCEc0WDJV+q8OI1Qzwn4OFbqsHTVtYoc4L7jJYtF6pEQYcbmlMMWBZjw0tJYuXeyiTQVboWIwAKg==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "@babel/plugin-syntax-function-bind": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.12.13.tgz", + "integrity": "sha512-8tkZMgbO5s/WkVnl04rBvULapZeXOHkaEW+w7oSzmEKwD6hDCtaAKouhgpoMa3uo8zC1HFpjlVh85PUVqvAxHw==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { + "@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "@babel/plugin-syntax-jsx": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", + "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.10.4" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { + "@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.10.4" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz", + "integrity": "sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { + "@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "@babel/plugin-transform-arrow-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.13.tgz", + "integrity": "sha512-tBtuN6qtCTd+iHzVZVOMNp+L04iIJBpqkdY42tWbmjIT5wvR2kx7gxMBsyhQtFzHwBbyGi9h8J8r9HgnOpQHxg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", - "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", + "@babel/plugin-transform-async-to-generator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.13.tgz", + "integrity": "sha512-psM9QHcHaDr+HZpRuJcE1PXESuGWSCcbiGFFhhwfzdbTxaGDVzuVtdNYliAwcRo3GFg0Bc8MmI+AvIGYIJG04A==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-remap-async-to-generator": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", + "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", + "@babel/plugin-transform-block-scoping": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz", + "integrity": "sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz", - "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==", + "@babel/plugin-transform-classes": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.13.tgz", + "integrity": "sha512-cqZlMlhCC1rVnxE5ZGMtIb896ijL90xppMiuWXcwcOAuFczynpd3KYemb91XFFPi3wJSe/OcrX9lXoowatkkxA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-remap-async-to-generator": "^7.25.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", - "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", - "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", - "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.4", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", "globals": "^11.1.0" }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", + "@babel/plugin-transform-computed-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.13.tgz", + "integrity": "sha512-dDfuROUPGK1mTtLKyDPUavmj2b6kFu82SmgpztBFEO974KMjJT+Ytj3/oWsTUMBmgPcp9J5Pc1SlcAYRpJ2hRA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", - "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", + "@babel/plugin-transform-destructuring": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.13.tgz", + "integrity": "sha512-Dn83KykIFzjhA3FDPA1z4N+yfF3btDGhjnJwxIj0T43tP0flCujnU8fKgEkf0C1biIpSv9NZegPBQ1J6jYkwvQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", + "@babel/plugin-transform-dotall-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", + "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", + "@babel/plugin-transform-duplicate-keys": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", + "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", - "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", + "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", + "@babel/plugin-transform-for-of": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.13.tgz", + "integrity": "sha512-xCbdgSzXYmHGyVX3+BsQjcd4hv4vA/FDy7Kc8eOpzKmBBPEOTurt0w5fCRQaGl+GSBORKgJdstQ1rHl4jbNseQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "@babel/plugin-transform-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", + "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", + "@babel/plugin-transform-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", + "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", + "@babel/plugin-transform-member-expression-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", + "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", - "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", + "@babel/plugin-transform-modules-amd": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.13.tgz", + "integrity": "sha512-JHLOU0o81m5UqG0Ulz/fPC68/v+UTuGTWaZBUwpEk1fYQ1D9LfKV6MPn4ttJKqRo5Lm460fkzjLTL4EHvCprvA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", + "@babel/plugin-transform-modules-commonjs": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.13.tgz", + "integrity": "sha512-OGQoeVXVi1259HjuoDnsQMlMkT9UkZT9TpXAsqWplS/M0N1g3TJAn/ByOCeQu7mfjc5WpSsRU+jV1Hd89ts0kQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", - "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", + "@babel/plugin-transform-modules-systemjs": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.13.tgz", + "integrity": "sha512-aHfVjhZ8QekaNF/5aNdStCGzwTbU7SI5hUybBKlMzqIMC7w7Ho8hx5a4R/DkTHfRfLwHGGxSpFt9BfxKCoXKoA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-hoist-variables": "^7.12.13", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", + "@babel/plugin-transform-modules-umd": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.13.tgz", + "integrity": "sha512-BgZndyABRML4z6ibpi7Z98m4EVLFI9tVsZDADC14AElFaNHHBcJIovflJ6wtCqFxwy2YJ1tJhGRsr0yLPKoN+w==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", + "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13" } }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", + "@babel/plugin-transform-new-target": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", + "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", - "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", + "@babel/plugin-transform-object-super": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", + "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13" + }, "dependencies": { - "@babel/helper-module-transforms": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-simple-access": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", - "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", + "@babel/plugin-transform-parameters": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.13.tgz", + "integrity": "sha512-e7QqwZalNiBRHCpJg/P8s/VJeSRYgmtWySs1JwvfwPqhBbiWfOcHDKdeAi6oAyIimoKWBlwc8oTgbZHdhCoVZA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-module-transforms": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", + "@babel/plugin-transform-property-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", + "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", + "@babel/plugin-transform-react-jsx": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.13.tgz", + "integrity": "sha512-hhXZMYR8t9RvduN2uW4sjl6MRtUhzNE726JvoJhpjhxKgRUVkZqTsA0xc49ALZxQM7H26pZ/lLvB2Yrea9dllA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/types": "^7.12.13" + }, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", + "@babel/plugin-transform-regenerator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz", + "integrity": "sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "requires": { + "regenerator-transform": "^0.14.2" } }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "@babel/plugin-transform-reserved-words": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", + "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", + "@babel/plugin-transform-runtime": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.15.tgz", + "integrity": "sha512-OwptMSRnRWJo+tJ9v9wgAf72ydXWfYSXWhnQjZing8nGZSDFqU1MBleKM3+DriKkcbv7RagA8gVeB0A1PNlNow==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "semver": "^5.5.1" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", + "@babel/plugin-transform-shorthand-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", + "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", + "@babel/plugin-transform-spread": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.13.tgz", + "integrity": "sha512-dUCrqPIowjqk5pXsx1zPftSq4sT0aCeZVAxhdgs3AMgyaDmoUT0G+5h3Dzja27t76aUEIJWlFgPJqJ/d4dbTtg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", + "@babel/plugin-transform-sticky-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", + "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", - "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", + "@babel/plugin-transform-template-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.13.tgz", + "integrity": "sha512-arIKlWYUgmNsF28EyfmiQHJLJFlAJNYkuQO10jL46ggjBpeb2re1P9K9YGxNJB45BqTbaslVysXDYm/g3sN/Qg==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", + "@babel/plugin-transform-typeof-symbol": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", + "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", - "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", + "@babel/plugin-transform-unicode-escapes": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", + "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", + "@babel/plugin-transform-unicode-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", + "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", "dev": true, - "license": "MIT", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + } } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", + "@babel/preset-env": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.13.tgz", + "integrity": "sha512-JUVlizG8SoFTz4LmVUL8++aVwzwxcvey3N0j1tRbMAXVEy95uQ/cnEkmEKHN00Bwq4voAV3imQGnQvpkLAxsrw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz", - "integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.25.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", - "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", - "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz", - "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.25.4", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", + "requires": { + "@babel/compat-data": "^7.12.13", + "@babel/helper-compilation-targets": "^7.12.13", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-validator-option": "^7.12.11", + "@babel/plugin-proposal-async-generator-functions": "^7.12.13", + "@babel/plugin-proposal-class-properties": "^7.12.13", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.13", + "@babel/plugin-proposal-json-strings": "^7.12.13", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.13", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.13", + "@babel/plugin-proposal-numeric-separator": "^7.12.13", + "@babel/plugin-proposal-object-rest-spread": "^7.12.13", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.13", + "@babel/plugin-proposal-optional-chaining": "^7.12.13", + "@babel/plugin-proposal-private-methods": "^7.12.13", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "@babel/plugin-syntax-async-generators": "^7.8.0", "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.25.4", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.4", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", - "semver": "^6.3.1" + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.12.13", + "@babel/plugin-transform-arrow-functions": "^7.12.13", + "@babel/plugin-transform-async-to-generator": "^7.12.13", + "@babel/plugin-transform-block-scoped-functions": "^7.12.13", + "@babel/plugin-transform-block-scoping": "^7.12.13", + "@babel/plugin-transform-classes": "^7.12.13", + "@babel/plugin-transform-computed-properties": "^7.12.13", + "@babel/plugin-transform-destructuring": "^7.12.13", + "@babel/plugin-transform-dotall-regex": "^7.12.13", + "@babel/plugin-transform-duplicate-keys": "^7.12.13", + "@babel/plugin-transform-exponentiation-operator": "^7.12.13", + "@babel/plugin-transform-for-of": "^7.12.13", + "@babel/plugin-transform-function-name": "^7.12.13", + "@babel/plugin-transform-literals": "^7.12.13", + "@babel/plugin-transform-member-expression-literals": "^7.12.13", + "@babel/plugin-transform-modules-amd": "^7.12.13", + "@babel/plugin-transform-modules-commonjs": "^7.12.13", + "@babel/plugin-transform-modules-systemjs": "^7.12.13", + "@babel/plugin-transform-modules-umd": "^7.12.13", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", + "@babel/plugin-transform-new-target": "^7.12.13", + "@babel/plugin-transform-object-super": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.12.13", + "@babel/plugin-transform-property-literals": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.12.13", + "@babel/plugin-transform-reserved-words": "^7.12.13", + "@babel/plugin-transform-shorthand-properties": "^7.12.13", + "@babel/plugin-transform-spread": "^7.12.13", + "@babel/plugin-transform-sticky-regex": "^7.12.13", + "@babel/plugin-transform-template-literals": "^7.12.13", + "@babel/plugin-transform-typeof-symbol": "^7.12.13", + "@babel/plugin-transform-unicode-escapes": "^7.12.13", + "@babel/plugin-transform-unicode-regex": "^7.12.13", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.12.13", + "core-js-compat": "^3.8.0", + "semver": "^5.5.0" }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/register": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.24.6.tgz", - "integrity": "sha512-WSuFCc2wCqMeXkz/i3yfAAsxwWflEgbVkZzivgAmXl/MxrXeoYFZOOPllbC8R8WTF7u61wSRQtDVZ1879cdu6w==", + "@babel/register": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.12.13.tgz", + "integrity": "sha512-fnCeRXj970S9seY+973oPALQg61TRvAaW0nRDe1f4ytKqM3fZgsNXewTZWmqZedg74LFIRpg/11dsrPZZvYs2g==", "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", + "requires": { "find-cache-dir": "^2.0.0", + "lodash": "^4.17.19", "make-dir": "^2.1.0", - "pirates": "^4.0.6", + "pirates": "^4.0.0", "source-map-support": "^0.5.16" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", - "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" + "@babel/runtime": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.13.tgz", + "integrity": "sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==", + "requires": { + "regenerator-runtime": "^0.13.4" } }, - "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "@babel/template": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" } }, - "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "@babel/traverse": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", - "debug": "^4.3.1", - "globals": "^11.1.0" + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" }, - "engines": { - "node": ">=6.9.0" + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, - "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "@babel/types": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@fortawesome/fontawesome-free": { + "version": "5.15.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.2.tgz", + "integrity": "sha512-7l/AX41m609L/EXI9EKH3Vs3v0iA8tKlIOGtw+kgcoanI7p+e4I4GYLqW3UXWiTnjSFymKSmTTPKYrivzbxxqA==" + }, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@fortawesome/fontawesome-free": { - "version": "5.15.4", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", - "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", - "hasInstallScript": true, - "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", - "engines": { - "node": ">=6" - } - }, - "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, - "license": "ISC", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/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, - "license": "MIT", - "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/@jest/console/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/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, - "license": "MIT" - }, - "node_modules/@jest/console/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true } } }, - "node_modules/@jest/core/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==", + "@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/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, - "license": "MIT", - "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/@jest/core/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/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, - "license": "MIT" - }, - "node_modules/@jest/core/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } } } }, - "node_modules/@jest/reporters/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==", + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", "dev": true, - "license": "MIT", + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", "dev": true, - "license": "MIT", - "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/@jest/reporters/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/reporters/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, - "license": "MIT" - }, - "node_modules/@jest/reporters/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/reporters/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", + "requires": { "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "dependencies": { + "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 + } } }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", "dev": true, - "license": "MIT", + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "@types/anymatch": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", + "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", + "dev": true }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "@types/babel__core": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", + "integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform/node_modules/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/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, - "license": "MIT", - "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/@jest/transform/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/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, - "license": "MIT" - }, - "node_modules/@jest/transform/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/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, - "license": "MIT", - "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/@jest/types/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/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, - "license": "MIT" - }, - "node_modules/@jest/types/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "@types/babel__generator": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", + "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/types": "^7.0.0" } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "@types/babel__traverse": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.8.tgz", + "integrity": "sha512-yGeB2dHEdvxjP0y4UbRtQaSkXJ9649fYCmIdRoul5kfAoGCwxuCbMhag0k3RPfnuh9kPGm8x89btcfDEXdVWGw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" + "requires": { + "@babel/types": "^7.3.0" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, - "license": "MIT" + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { + "@types/events": "*", "@types/minimatch": "*", "@types/node": "*" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", - "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", - "license": "MIT", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@types/istanbul-lib-coverage": "*" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { + "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", + "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==", + "dev": true + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/tapable": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz", + "integrity": "sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ==", + "dev": true + }, + "@types/uglify-js": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz", + "integrity": "sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", - "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", - "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-redux": { - "version": "7.1.33", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz", - "integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==", - "license": "MIT", - "dependencies": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, - "node_modules/@types/source-list-map": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.6.tgz", - "integrity": "sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/tapable": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.12.tgz", - "integrity": "sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/uglify-js": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.5.tgz", - "integrity": "sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "source-map": "^0.6.1" + }, + "dependencies": { + "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 + } } }, - "node_modules/@types/webpack": { - "version": "4.41.39", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.39.tgz", - "integrity": "sha512-otxUJvoi6FbBq/64gGH34eblpKLgdi+gf08GaAh8Bx6So0ZZic028Ev/SUxD22gbthMKCkeeiXEat1kHLDJfYg==", + "@types/webpack": { + "version": "4.41.8", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.8.tgz", + "integrity": "sha512-mh4litLHTlDG84TGCFv1pZldndI34vkrW9Mks++Zx4KET7DRMoCXUvLbTISiuF4++fMgNnhV9cc1nCXJQyBYbQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { + "@types/anymatch": "*", "@types/node": "*", - "@types/tapable": "^1", + "@types/tapable": "*", "@types/uglify-js": "*", "@types/webpack-sources": "*", - "anymatch": "^3.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "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 + } } }, - "node_modules/@types/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==", + "@types/webpack-sources": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.7.tgz", + "integrity": "sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@types/node": "*", "@types/source-list-map": "*", - "source-map": "^0.7.3" - } - }, - "node_modules/@types/webpack-sources/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", + "source-map": "^0.6.1" + }, "dependencies": { + "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 + } + } + }, + "@types/yargs": { + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.4.tgz", + "integrity": "sha512-Ke1WmBbIkVM8bpvsNEcGgQM70XcEh/nbpxQhW7FhrsbCsXSY9BmLB1+LHtD7r9zrsOcFlLiF+a/UeJsdfw3C5A==", + "dev": true, + "requires": { "@types/yargs-parser": "*" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" + "@types/yargs-parser": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", + "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", + "dev": true }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true, - "license": "MIT" + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true, - "license": "MIT" + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true, - "license": "MIT" + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true, - "license": "MIT" + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "requires": { + "@webassemblyjs/ast": "1.9.0" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { + "requires": { "@xtuc/long": "4.2.2" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true, - "license": "MIT" + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", "@xtuc/long": "4.2.2" } }, - "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" } }, - "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { + "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, - "node_modules/@xtuc/long": { + "@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" + "dev": true }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "abab": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true }, - "node_modules/ansi-styles": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, - "node_modules/array-union": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, + "array-find-index": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true }, - "node_modules/array-uniq": { + "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "requires": { + "safer-buffer": "~2.1.0" } }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "dev": true, - "license": "MIT", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } } }, - "node_modules/babel-jest/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==", + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", "dev": true, - "license": "MIT", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } } }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true, - "license": "MIT", + "optional": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", + "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==", + "dev": true + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + }, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } } }, - "node_modules/babel-jest/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==", + "babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/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, - "license": "MIT" - }, - "node_modules/babel-jest/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", + "loader-utils": "^1.4.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" + "dependencies": { + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", "dev": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "requires": { + "object.assign": "^4.1.0" } }, - "node_modules/babel-loader/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==", + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "license": "BSD-3-Clause", - "dependencies": { + "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", + "requires": { "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" } }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-runtime": { + "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + } } }, - "node_modules/babel-runtime/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.", - "dev": true, - "hasInstallScript": true, - "license": "MIT" + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, - "license": "MIT" + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } }, - "node_modules/balanced-match": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, - "license": "MIT" + "requires": { + "tweetnacl": "^0.14.3" + } }, - "node_modules/big.js": { + "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, - "license": "MIT", - "engines": { - "node": "*" + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "requires": { + "inherits": "~2.0.0" } }, - "node_modules/brace-expansion": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "dev": true + }, + "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bser": { + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001181", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.649", + "escalade": "^3.1.1", + "node-releases": "^1.1.70" + } + }, + "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { + "requires": { "node-int64": "^0.4.0" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, - "license": "MIT" + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } }, - "node_modules/callsites": { + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "caniuse-lite": { + "version": "1.0.30001185", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001185.tgz", + "integrity": "sha512-Fpi4kVNtNvJ15H0F6vwmXtb3tukv3Zg3qhKkOGUq7KJ1J6b9kf4dnNgtEAFXhRsJo0gNj9W60+wBvn0JcTvdTg==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "requires": { + "rsvp": "^4.8.4" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001651", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", - "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, - "node_modules/chalk": { + "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", + "optional": true, + "requires": { + "anymatch": "~3.1.1", "braces": "~3.0.2", - "glob-parent": "~5.1.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "~3.5.0" }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "optional": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "optional": true, + "requires": { + "is-number": "^7.0.0" + } } - ], - "license": "MIT", - "engines": { - "node": ">=8" } }, - "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true, - "license": "MIT" + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true }, - "node_modules/clean-webpack-plugin": { + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-webpack-plugin": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz", "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@types/webpack": "^4.4.31", "del": "^4.1.1" }, - "engines": { - "node": ">=8.9.0" - }, - "peerDependencies": { - "webpack": "*" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, - "node_modules/clone-deep": { + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" } }, - "node_modules/co": { + "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { + "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "color-name": "1.1.3" } }, - "node_modules/color-name": { + "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true }, - "node_modules/commander": { + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" + "dev": true }, - "node_modules/commondir": { + "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true, - "license": "MIT" + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true }, - "node_modules/concat-map": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, - "license": "MIT" - }, - "node_modules/core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, - "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", "dev": true, - "license": "MIT", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz", + "integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==", + "dev": true + }, + "core-js-compat": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.3.tgz", + "integrity": "sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog==", + "dev": true, + "requires": { + "browserslist": "^4.16.1", + "semver": "7.0.0" + }, "dependencies": { - "browserslist": "^4.23.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/create-jest/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, - "license": "MIT", - "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/create-jest/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/create-jest/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, - "license": "MIT" - }, - "node_modules/create-jest/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/create-jest/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-loader": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", - "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.27.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true } } }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" }, - "engines": { - "node": ">=10" + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } } }, - "node_modules/cssesc": { + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" + "requires": { + "cssom": "0.3.x" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, - "license": "MIT", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } } } }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "requires": { + "ms": "2.0.0" } }, - "node_modules/deep-diff": { + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-diff": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", - "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==", - "license": "MIT" + "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "requires": { + "object-keys": "^1.0.12" } }, - "node_modules/del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, - "license": "MIT", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, "dependencies": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" }, - "engines": { - "node": ">=6" + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "requires": { + "webidl-conversions": "^4.0.2" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, - "license": "ISC" + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "electron-to-chromium": { + "version": "1.3.657", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.657.tgz", + "integrity": "sha512-/9ROOyvEflEbaZFUeGofD+Tqs/WynbSTbNgNF+/TJJxH1ePD/e6VjZlDJpW3FFFd3nj5l3Hd8ki2vRwy+gyRFw==", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, - "node_modules/emojis-list": { + "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" + "requires": { + "once": "^1.4.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", "dev": true, - "license": "MIT", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } } }, - "node_modules/envinfo": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" + "requires": { + "prr": "~1.0.1" } }, - "node_modules/error-ex": { + "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "is-arrayish": "^0.2.1" } }, - "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "es-abstract": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz", + "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==", "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } } }, - "node_modules/escape-string-regexp": { + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", "dev": true, - "license": "BSD-2-Clause", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" + "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, + "optional": true + } } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, - "node_modules/esrecurse": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { + "requires": { "estraverse": "^5.2.0" }, - "engines": { - "node": ">=4.0" + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { + "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } + "dev": true }, - "node_modules/esutils": { + "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, - "license": "MIT", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } } }, - "node_modules/exit": { + "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, - "license": "MIT" + "requires": { + "homedir-polyfill": "^1.0.1" + } }, - "node_modules/fast-json-stable-stringify": { + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" + "dev": true }, - "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", - "dev": true, - "license": "MIT" + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { + "requires": { "bser": "2.1.1" } }, - "node_modules/fetch-mock": { + "fetch-mock": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-8.3.2.tgz", "integrity": "sha512-RUdLbhIBTvECX20I8htNhmLRrCplCiOP62srst8UQsSV0m8taJe31PBsQybL7OIq5fEf6tnqVGvQ62ZnZ4IFfQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "babel-runtime": "^6.26.0", "core-js": "^3.0.0", "glob-to-regexp": "^0.4.0", @@ -4691,4880 +5369,6035 @@ "path-to-regexp": "^2.2.1", "querystring": "^0.2.0", "whatwg-url": "^6.5.0" - }, - "engines": { - "node": ">=4.0.0" - }, - "funding": { - "type": "charity", - "url": "https://www.justgiving.com/refugee-support-europe" - }, - "peerDependencies": { - "node-fetch": "*" - }, - "peerDependenciesMeta": { - "node-fetch": { - "optional": true - } } }, - "node_modules/file-loader": { + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "file-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { + "requires": { "loader-utils": "^2.0.0", "schema-utils": "^3.0.0" }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" + "optional": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, - "engines": { - "node": ">=8" + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, - "node_modules/find-cache-dir": { + "find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "commondir": "^1.0.1", "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" } }, - "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, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "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, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/globby/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "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, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-path-inside": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-is-inside": "^1.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "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, - "license": "BSD-3-Clause", - "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-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, - "license": "BSD-3-Clause", - "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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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, - "license": "MIT", - "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.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/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, - "license": "MIT", - "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/jest-circus/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/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, - "license": "MIT" - }, - "node_modules/jest-circus/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/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, - "license": "MIT", - "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/jest-cli/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/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, - "license": "MIT" - }, - "node_modules/jest-cli/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/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, - "license": "MIT", - "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/jest-config/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/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, - "license": "MIT" - }, - "node_modules/jest-config/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/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, - "license": "MIT", - "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/jest-diff/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/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, - "license": "MIT" - }, - "node_modules/jest-diff/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/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, - "license": "MIT", - "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/jest-each/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/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, - "license": "MIT" - }, - "node_modules/jest-each/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/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, - "license": "MIT", - "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/jest-matcher-utils/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/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, - "license": "MIT" - }, - "node_modules/jest-matcher-utils/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/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, - "license": "MIT", - "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/jest-message-util/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/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, - "license": "MIT" - }, - "node_modules/jest-message-util/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/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, - "license": "MIT", - "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/jest-resolve/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/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, - "license": "MIT" - }, - "node_modules/jest-resolve/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/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, - "license": "MIT", - "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/jest-runner/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/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, - "license": "MIT" - }, - "node_modules/jest-runner/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runner/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/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, - "license": "MIT", - "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/jest-runtime/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/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, - "license": "MIT" - }, - "node_modules/jest-runtime/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/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, - "license": "MIT", - "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/jest-snapshot/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/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, - "license": "MIT" - }, - "node_modules/jest-snapshot/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/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, - "license": "MIT", - "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/jest-util/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/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, - "license": "MIT" - }, - "node_modules/jest-util/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/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, - "license": "MIT", - "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/jest-validate/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/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, - "license": "MIT" - }, - "node_modules/jest-validate/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/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, - "license": "MIT", - "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/jest-watcher/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/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, - "license": "MIT" - }, - "node_modules/jest-watcher/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-cookie": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", - "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", - "license": "MIT" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "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, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", - "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true, - "license": "(WTFPL OR MIT)" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", - "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { + "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" } }, - "node_modules/pkg-dir/node_modules/locate-path": { + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "^1.0.0" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "dev": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", + "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-escaper": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.1.tgz", + "integrity": "sha512-hNX23TjWwD3q56HpWjUHOKj1+4KKlnjv9PcmBUYKVpga+2cnb9nDx/B1o0yO4n+RZXZdiNxzx6B24C9aNMTkkQ==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", + "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + }, + "dependencies": { + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + } + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "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 + } + } + }, + "istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0" + } + }, + "jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, + "requires": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" } }, - "node_modules/pkg-dir/node_modules/p-locate": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "mime-db": { + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "dev": true, + "requires": { + "mime-db": "1.42.0" + } + }, + "mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node-releases": { + "version": "1.1.70", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", + "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==", + "dev": true + }, + "node-sass": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", + "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "2.2.5", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" } }, - "node_modules/pkg-dir/node_modules/path-exists": { + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, - "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", + "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true, - "license": "MIT", + "optional": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, "dependencies": { - "icss-utils": "^5.0.0", + "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 + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", "postcss-selector-parser": "^6.0.2", "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", "dev": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" } }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", "dev": true, - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" } }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "postcss-selector-parser": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", + "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1", "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true }, - "node_modules/prettier": { + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "prettier": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=4" - } + "dev": true }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", "dev": true, - "license": "MIT", + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prompts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.0.tgz", + "integrity": "sha512-NfbbPPg/74fT7wk2XYQ7hAIp9zJyZp5Fu19iRbORqqy1BhtrkZ0fPafBU+7bmn8ie69DpT0R6QpJIN2oisYjJg==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" + "sisteransi": "^1.0.3" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "react-is": "^16.8.1" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "psl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } - ], - "license": "MIT" - }, - "node_modules/querystring": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", - "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.x" } }, - "node_modules/randombytes": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "safe-buffer": "^5.1.0" } }, - "node_modules/react": { + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "react": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "license": "MIT", - "dependencies": { + "dev": true, + "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" } }, - "node_modules/react-dom": { + "react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", "scheduler": "^0.19.1" - }, - "peerDependencies": { - "react": "^16.14.0" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" }, - "node_modules/react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", + "react-redux": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", + "integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==", + "requires": { + "@babel/runtime": "^7.12.1", "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", - "react-is": "^17.0.2" + "react-is": "^16.13.1" }, - "peerDependencies": { - "react": "^16.8.3 || ^17 || ^18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" } } }, - "node_modules/react-redux/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, - "license": "MIT", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "optional": true, + "requires": { "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" } }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" + "requires": { + "util.promisify": "^1.0.0" } }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" } }, - "node_modules/redux-logger": { + "redux": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-logger": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", - "integrity": "sha512-JoCIok7bg/XpqA1JqCqXFypuqBbQzGQySrhFzewB7ThcnysTO30l4VCst86AuB9T9tuT03MAA56Jw2PNhRSNCg==", - "license": "MIT", - "dependencies": { + "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=", + "requires": { "deep-diff": "^0.3.5" } }, - "node_modules/redux-mock-store": { + "redux-mock-store": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz", "integrity": "sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "lodash.isplainobject": "^4.0.6" } }, - "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "license": "MIT", - "peerDependencies": { - "redux": "^4" - } + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" }, - "node_modules/regenerate": { + "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT" + "dev": true }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" + "requires": { + "regenerate": "^1.4.0" } }, - "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==", - "license": "MIT" + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "@babel/runtime": "^7.8.4" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "regjsparser": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.7.tgz", + "integrity": "sha512-ib77G0uxsA2ovgiYbCVGx4Pv3PSttAx2vIwidqQzbL2U5S4Q+j00HdSAneSBuyVcMvEnTXMjiGgB+DlXozVhpQ==", + "dev": true, + "requires": { "jsesc": "~0.5.0" }, - "bin": { - "regjsparser": "bin/parser" + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "requires": { + "is-finite": "^1.0.0" } }, - "node_modules/require-directory": { + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "request-promise-native": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "dev": true, + "requires": { + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", + "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "requires": { + "path-parse": "^1.0.6" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "requires": { + "resolve-from": "^3.0.0" } }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, - "license": "MIT", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } } }, - "node_modules/resolve-cwd": { + "resolve-from": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true }, - "node_modules/rimraf": { + "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "ISC", - "dependencies": { + "requires": { "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "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" - } - ], - "license": "MIT" - }, - "node_modules/sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, - "node_modules/sass-loader": { + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "sass-graph": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", + "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^13.3.2" + } + }, + "sass-loader": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "clone-deep": "^4.0.1", "loader-utils": "^1.2.3", "neo-async": "^2.6.1", "schema-utils": "^2.6.1", "semver": "^6.3.0" }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0", - "sass": "^1.3.0", - "webpack": "^4.36.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, - "node_modules/sass-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true }, - "node_modules/sass-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/scheduler": { + "scheduler": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, - "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", "dev": true, - "license": "MIT", + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "ajv": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + } } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { + "requires": { "randombytes": "^2.1.0" } }, - "node_modules/shallow-clone": { + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { + "shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" + "requires": { + "shebang-regex": "^1.0.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sisteransi": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", + "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, - "license": "MIT", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "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 + } } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/strip-ansi": { + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", - "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", - "dev": true, - "license": "MIT", "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", + "dev": true, + "requires": { "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" + "schema-utils": "^2.7.0" }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/style-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + } } }, - "node_modules/supports-color": { + "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.31.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", - "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "requires": { "commander": "^2.20.0", - "source-map-support": "~0.5.20" + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true + "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 } } }, - "node_modules/terser-webpack-plugin/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==", + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "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 + } } }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", "dev": true, - "license": "MIT", + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } } }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "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==", + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" + "requires": { + "setimmediate": "^1.0.4" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true }, - "node_modules/to-fast-properties": { + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" + "requires": { + "kind-of": "^3.0.2" }, - "engines": { - "node": ">=8.0" + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, - "node_modules/tr46": { + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "punycode": "^2.1.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "license": "MIT", "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "requires": { + "glob": "^7.1.2" } }, - "node_modules/url-loader": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "optional": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "loader-utils": "^2.0.0", "mime-types": "^2.1.27", "schema-utils": "^3.0.0" }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "file-loader": "*", - "webpack": "^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "file-loader": { - "optional": true + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "dev": true + }, + "mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "dev": true, + "requires": { + "mime-db": "1.45.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } } } }, - "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", "dev": true, - "license": "MIT", + "requires": { + "inherits": "2.0.3" + }, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } } }, - "node_modules/util-deprecate": { + "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", "dependencies": { - "makeerror": "1.0.12" + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "optional": true + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + } } }, - "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webidl-conversions": { + "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true, - "license": "BSD-2-Clause" + "dev": true }, - "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } } } }, - "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "webpack-cli": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz", + "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==", "dev": true, - "license": "MIT", + "requires": { + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.1", + "findup-sync": "^3.0.0", + "global-modules": "^2.0.0", + "import-local": "^2.0.0", + "interpret": "^1.4.0", + "loader-utils": "^1.4.0", + "supports-color": "^6.1.0", + "v8-compile-cache": "^2.1.1", + "yargs": "^13.3.2" + }, "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", - "colorette": "^2.0.14", - "commander": "^10.0.1", - "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-cli/node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-merge": { + "webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "lodash": "^4.17.15" } }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "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 + } } }, - "node_modules/whatwg-url": { + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "dev": true, - "license": "MIT", - "dependencies": { + "requires": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "license": "ISC", - "dependencies": { + "requires": { "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" } }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, - "license": "MIT" + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "license": "MIT", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, - "node_modules/wrap-ansi/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/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, - "license": "MIT" - }, - "node_modules/wrappy": { + "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", "dev": true, - "license": "ISC", - "dependencies": { + "requires": { + "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "signal-exit": "^3.0.2" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" + "requires": { + "async-limiter": "~1.0.0" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" }, - "engines": { - "node": ">=12" + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } } } } diff --git a/package.json b/package.json index d12a88a..a2e593c 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,20 @@ { "name": "newsreader", - "version": "0.5.3", + "version": "0.3.13.5", "description": "Application for viewing RSS feeds", + "main": "index.js", "scripts": { "lint": "npx prettier \"src/newsreader/js/**/*.js\" --check", "format": "npx prettier \"src/newsreader/js/**/*.js\" --write", + "build": "npx webpack --config webpack.dev.babel.js", "build:watch": "npx webpack --config webpack.dev.babel.js --watch", - "build:dev": "npx webpack --config webpack.dev.babel.js", "build:prod": "npx webpack --config webpack.prod.babel.js", "test": "npx jest", "test:watch": "npm test -- --watch" }, "repository": { "type": "git", - "url": "forgejo.fudiggity.nl:sonny/newsreader" + "url": "[git@git.fudiggity.nl:5000]:sonny/newsreader.git" }, "author": "Sonny", "license": "GPL-3.0-or-later", @@ -31,45 +32,32 @@ "@babel/core": "^7.12.13", "@babel/plugin-proposal-class-properties": "^7.12.13", "@babel/plugin-proposal-function-bind": "^7.12.13", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-function-bind": "^7.12.13", "@babel/plugin-transform-react-jsx": "^7.12.13", + "@babel/plugin-transform-runtime": "^7.12.15", "@babel/preset-env": "^7.12.13", "@babel/register": "^7.12.13", "@babel/runtime": "^7.12.13", - "babel-jest": "^29.7.0", + "babel-jest": "^24.9.0", "babel-loader": "^8.2.2", "clean-webpack-plugin": "^3.0.0", - "css-loader": "^7.1.2", + "css-loader": "^3.6.0", "fetch-mock": "^8.3.2", - "jest": "^29.7.0", - "mini-css-extract-plugin": "^2.9.1", + "file-loader": "^6.2.0", + "jest": "^24.9.0", + "mini-css-extract-plugin": "^0.9.0", "node-fetch": "^2.6.1", + "node-sass": "^4.14.1", "prettier": "^1.19.1", "react": "^16.14.0", "react-dom": "^16.14.0", "redux-mock-store": "^1.5.4", - "sass": "^1.52.1", "sass-loader": "^8.0.2", - "style-loader": "^2.0.0", + "style-loader": "^1.3.0", "url-loader": "^4.1.1", - "webpack": "^5.94.0", - "webpack-cli": "^5.1.4", + "webpack": "^4.46.0", + "webpack-cli": "^3.3.12", "webpack-merge": "^4.2.2" - }, - "prettier": { - "semi": true, - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 90, - "tabWidth": 2, - "useTabs": false, - "bracketSpacing": true, - "arrowParens": "avoid" - }, - "jest": { - "roots": [ - "src/newsreader/js/tests/" - ], - "clearMocks": true, - "coverageDirectory": "coverage" } } diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..08ca26a --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1296 @@ +[[package]] +name = "amqp" +version = "2.6.1" +description = "Low-level AMQP client for Python (fork of amqplib)." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +vine = ">=1.1.3,<5.0.0a1" + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "asgiref" +version = "3.3.4" +description = "ASGI specs, helper code, and adapters" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] + +[[package]] +name = "attrs" +version = "20.3.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +name = "autoflake" +version = "1.3.1" +description = "Removes unused imports and unused variables" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +pyflakes = ">=1.1.0" + +[[package]] +name = "beautifulsoup4" +version = "4.9.3" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "billiard" +version = "3.6.4.0" +description = "Python multiprocessing fork with improvements and bugfixes" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "black" +version = "19.3b0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +appdirs = "*" +attrs = ">=18.1.0" +click = ">=6.5" +toml = ">=0.9.4" + +[package.extras] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] + +[[package]] +name = "bleach" +version = "3.3.0" +description = "An easy safelist-based HTML-sanitizing tool." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +packaging = "*" +six = ">=1.9.0" +webencodings = "*" + +[[package]] +name = "celery" +version = "4.4.7" +description = "Distributed Task Queue." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +billiard = ">=3.6.3.0,<4.0" +kombu = ">=4.6.10,<4.7" +pytz = ">0.0-dev" +vine = "1.3.0" + +[package.extras] +arangodb = ["pyArango (>=1.3.2)"] +auth = ["cryptography"] +azureblockblob = ["azure-storage (==0.36.0)", "azure-common (==1.1.5)", "azure-storage-common (==1.1.0)"] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +cassandra = ["cassandra-driver (<3.21.0)"] +consul = ["python-consul"] +cosmosdbsql = ["pydocumentdb (==2.3.2)"] +couchbase = ["couchbase-cffi (<3.0.0)", "couchbase (<3.0.0)"] +couchdb = ["pycouchdb"] +django = ["Django (>=1.11)"] +dynamodb = ["boto3 (>=1.9.178)"] +elasticsearch = ["elasticsearch"] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent"] +librabbitmq = ["librabbitmq (>=1.5.0)"] +lzma = ["backports.lzma"] +memcache = ["pylibmc"] +mongodb = ["pymongo[srv] (>=3.3.0)"] +msgpack = ["msgpack"] +pymemcache = ["python-memcached"] +pyro = ["pyro4"] +redis = ["redis (>=3.2.0)"] +riak = ["riak (>=2.0)"] +s3 = ["boto3 (>=1.9.125)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +solar = ["ephem"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.9.125)", "pycurl (==7.43.0.5)"] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] +zstd = ["zstandard"] + +[[package]] +name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "chardet" +version = "4.0.0" +description = "Universal encoding detector for Python 2 and 3" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coreapi" +version = "2.3.3" +description = "Python client library for Core API." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +coreschema = "*" +itypes = "*" +requests = "*" +uritemplate = "*" + +[[package]] +name = "coreschema" +version = "0.0.4" +description = "Core Schema." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +jinja2 = "*" + +[[package]] +name = "coverage" +version = "5.5" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +toml = ["toml"] + +[[package]] +name = "django" +version = "3.2" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +asgiref = ">=3.3.2,<4" +pytz = "*" +sqlparse = ">=0.2.2" + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "django-axes" +version = "5.14.0" +description = "Keep track of failed login attempts in Django-powered sites." +category = "main" +optional = false +python-versions = "~=3.6" + +[package.dependencies] +django = ">=2.2" +django-ipware = ">=3,<4" + +[[package]] +name = "django-celery-beat" +version = "2.2.0" +description = "Database-backed Periodic Tasks." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +celery = ">=4.4,<6.0" +Django = ">=2.2,<4.0" +django-timezone-field = ">=4.1.0,<5.0" +python-crontab = ">=2.3.4" + +[[package]] +name = "django-debug-toolbar" +version = "2.2.1" +description = "A configurable set of panels that display various debug information about the current request/response." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +Django = ">=1.11" +sqlparse = ">=0.2.0" + +[[package]] +name = "django-extensions" +version = "2.2.9" +description = "Extensions for Django" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.2" + +[[package]] +name = "django-ipware" +version = "3.0.2" +description = "A Django utility application that returns client's real IP address" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "django-registration-redux" +version = "2.9" +description = "An extensible user-registration application for Django" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "django-timezone-field" +version = "4.1.2" +description = "A Django app providing database and form fields for pytz timezone objects." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +django = ">=2.2" +pytz = "*" + +[package.extras] +rest_framework = ["djangorestframework (>=3.0.0)"] + +[[package]] +name = "djangorestframework" +version = "3.12.4" +description = "Web APIs for Django, made easy." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +django = ">=2.2" + +[[package]] +name = "drf-yasg" +version = "1.20.0" +description = "Automated generation of real Swagger/OpenAPI 2.0 schemas from Django Rest Framework code." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coreapi = ">=2.3.3" +coreschema = ">=0.0.4" +Django = ">=2.2.16" +djangorestframework = ">=3.10.3" +inflection = ">=0.3.1" +packaging = "*" +"ruamel.yaml" = ">=0.15.34" +uritemplate = ">=3.0.0" + +[package.extras] +validation = ["swagger-spec-validator (>=2.1.0)"] + +[[package]] +name = "factory-boy" +version = "2.12.0" +description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +Faker = ">=0.7.0" + +[[package]] +name = "faker" +version = "8.1.1" +description = "Faker is a Python package that generates fake data for you." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +python-dateutil = ">=2.4" +text-unidecode = "1.3" + +[[package]] +name = "feedparser" +version = "5.2.1" +description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "freezegun" +version = "0.3.15" +description = "Let your Python tests travel through time" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +python-dateutil = ">=1.0,<2.0 || >2.0" +six = "*" + +[[package]] +name = "ftfy" +version = "5.9" +description = "Fixes some problems with Unicode text after the fact" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "importlib-metadata" +version = "4.0.1" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "inflection" +version = "0.5.1" +description = "A port of Ruby on Rails inflector to Python" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "isort" +version = "4.3.21" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +pipfile = ["pipreqs", "requirementslib"] +pyproject = ["toml"] +requirements = ["pipreqs", "pip-api"] +xdg_home = ["appdirs (>=1.4.0)"] + +[[package]] +name = "itypes" +version = "1.2.0" +description = "Simple immutable types for python." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "jinja2" +version = "2.11.3" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[package.extras] +i18n = ["Babel (>=0.8)"] + +[[package]] +name = "kombu" +version = "4.6.11" +description = "Messaging library for Python." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +amqp = ">=2.6.0,<2.7" +importlib-metadata = {version = ">=0.18", markers = "python_version < \"3.8\""} + +[package.extras] +azureservicebus = ["azure-servicebus (>=0.21.1)"] +azurestoragequeues = ["azure-storage-queue"] +consul = ["python-consul (>=0.6.0)"] +librabbitmq = ["librabbitmq (>=1.5.2)"] +mongodb = ["pymongo (>=3.3.0)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=3.3.11)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.4.4)", "pycurl (==7.43.0.2)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] + +[[package]] +name = "lxml" +version = "4.6.3" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["beautifulsoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" + +[[package]] +name = "oauthlib" +version = "3.1.0" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +rsa = ["cryptography"] +signals = ["blinker"] +signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] + +[[package]] +name = "packaging" +version = "20.9" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[package]] +name = "psycopg2-binary" +version = "2.8.6" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" + +[[package]] +name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "python-crontab" +version = "2.5.1" +description = "Python Crontab API" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +python-dateutil = "*" + +[package.extras] +cron-description = ["cron-descriptor"] +cron-schedule = ["croniter"] + +[[package]] +name = "python-dateutil" +version = "2.8.1" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "0.12.0" +description = "Add .env support to your django/flask apps in development and deployments" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-memcached" +version = "1.59" +description = "Pure python memcached client" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.4.0" + +[[package]] +name = "pytz" +version = "2021.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "requests" +version = "2.25.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<5" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.0" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "ruamel.yaml" +version = "0.17.4" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "main" +optional = false +python-versions = ">=3" + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.1.2", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.10\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel.yaml.clib" +version = "0.2.2" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "sentry-sdk" +version = "1.0.0" +description = "Python client for Sentry (https://sentry.io)" +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +certifi = "*" +urllib3 = ">=1.10.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +flask = ["flask (>=0.11)", "blinker (>=1.1)"] +pure_eval = ["pure-eval", "executing", "asttokens"] +pyspark = ["pyspark (>=2.4.4)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "soupsieve" +version = "2.2.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "sqlparse" +version = "0.4.1" +description = "A non-validating SQL parser." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "tblib" +version = "1.6.0" +description = "Traceback serialization library." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "typing-extensions" +version = "3.7.4.3" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "uritemplate" +version = "3.0.1" +description = "URI templates" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "urllib3" +version = "1.26.4" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotlipy (>=0.6.0)"] + +[[package]] +name = "vine" +version = "1.3.0" +description = "Promises, promises, promises." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "zipp" +version = "3.4.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[extras] +sentry = ["sentry-sdk"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.7" +content-hash = "a1c09a962a6c136cb9e60e5961a59cdf93fb1101966b4d65271f7804e21e45d1" + +[metadata.files] +amqp = [ + {file = "amqp-2.6.1-py2.py3-none-any.whl", hash = "sha256:aa7f313fb887c91f15474c1229907a04dac0b8135822d6603437803424c0aa59"}, + {file = "amqp-2.6.1.tar.gz", hash = "sha256:70cdb10628468ff14e57ec2f751c7aa9e48e7e3651cfd62d431213c0c4e58f21"}, +] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +asgiref = [ + {file = "asgiref-3.3.4-py3-none-any.whl", hash = "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee"}, + {file = "asgiref-3.3.4.tar.gz", hash = "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"}, +] +attrs = [ + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, +] +autoflake = [ + {file = "autoflake-1.3.1.tar.gz", hash = "sha256:680cb9dade101ed647488238ccb8b8bfb4369b53d58ba2c8cdf7d5d54e01f95b"}, +] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, +] +billiard = [ + {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, + {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, +] +black = [ + {file = "black-19.3b0-py36-none-any.whl", hash = "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf"}, + {file = "black-19.3b0.tar.gz", hash = "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"}, +] +bleach = [ + {file = "bleach-3.3.0-py2.py3-none-any.whl", hash = "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125"}, + {file = "bleach-3.3.0.tar.gz", hash = "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"}, +] +celery = [ + {file = "celery-4.4.7-py2.py3-none-any.whl", hash = "sha256:a92e1d56e650781fb747032a3997d16236d037c8199eacd5217d1a72893bca45"}, + {file = "celery-4.4.7.tar.gz", hash = "sha256:d220b13a8ed57c78149acf82c006785356071844afe0b27012a4991d44026f9f"}, +] +certifi = [ + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, +] +chardet = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] +coreapi = [ + {file = "coreapi-2.3.3-py2.py3-none-any.whl", hash = "sha256:bf39d118d6d3e171f10df9ede5666f63ad80bba9a29a8ec17726a66cf52ee6f3"}, + {file = "coreapi-2.3.3.tar.gz", hash = "sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb"}, +] +coreschema = [ + {file = "coreschema-0.0.4-py2-none-any.whl", hash = "sha256:5e6ef7bf38c1525d5e55a895934ab4273548629f16aed5c0a6caa74ebf45551f"}, + {file = "coreschema-0.0.4.tar.gz", hash = "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607"}, +] +coverage = [ + {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, + {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, + {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, + {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, + {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, + {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, + {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, + {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, + {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, + {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, + {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, + {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, + {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, + {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, + {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, + {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, + {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, + {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, + {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, + {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, + {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, +] +django = [ + {file = "Django-3.2-py3-none-any.whl", hash = "sha256:0604e84c4fb698a5e53e5857b5aea945b2f19a18f25f10b8748dbdf935788927"}, + {file = "Django-3.2.tar.gz", hash = "sha256:21f0f9643722675976004eb683c55d33c05486f94506672df3d6a141546f389d"}, +] +django-axes = [ + {file = "django-axes-5.14.0.tar.gz", hash = "sha256:30e60f980c9168eff3e4ae4b33d2a05d7f36868aa0a82a3d5ac74577be57fa56"}, + {file = "django_axes-5.14.0-py3-none-any.whl", hash = "sha256:70711f31e1d9eb319950063b25f80417f9d7ce1bd6e25438da885c5b1a48a365"}, +] +django-celery-beat = [ + {file = "django-celery-beat-2.2.0.tar.gz", hash = "sha256:b8a13afb15e7c53fc04f4f847ac71a6d32088959aba701eb7c4a59f0c28ba543"}, + {file = "django_celery_beat-2.2.0-py2.py3-none-any.whl", hash = "sha256:c4c72a9579f20eff4c4ccf1b58ebdca5ef940f4210065057db1754ea5f8dffdc"}, +] +django-debug-toolbar = [ + {file = "django-debug-toolbar-2.2.1.tar.gz", hash = "sha256:7aadab5240796ffe8e93cc7dfbe2f87a204054746ff7ff93cd6d4a0c3747c853"}, + {file = "django_debug_toolbar-2.2.1-py3-none-any.whl", hash = "sha256:7feaee934608f5cdd95432154be832fe30fda6c1249018191e2c27bc0b6a965e"}, +] +django-extensions = [ + {file = "django-extensions-2.2.9.tar.gz", hash = "sha256:2f81b618ba4d1b0e58603e25012e5c74f88a4b706e0022a3b21f24f0322a6ce6"}, + {file = "django_extensions-2.2.9-py2.py3-none-any.whl", hash = "sha256:b19182d101a441fe001c5753553a901e2ef3ff60e8fbbe38881eb4a61fdd17c4"}, +] +django-ipware = [ + {file = "django-ipware-3.0.2.tar.gz", hash = "sha256:c7df8e1410a8e5d6b1fbae58728402ea59950f043c3582e033e866f0f0cf5e94"}, +] +django-registration-redux = [ + {file = "django-registration-redux-2.9.tar.gz", hash = "sha256:e3d123354a1b8cbfa005d60f1ebb89ae8541f3eaffd6174d9f2aff529b57e430"}, + {file = "django_registration_redux-2.9-py2.py3-none-any.whl", hash = "sha256:e94b8a945e1cbfa9ec6c32b549597270405328d4e26651985d287d0211120691"}, +] +django-timezone-field = [ + {file = "django-timezone-field-4.1.2.tar.gz", hash = "sha256:cffac62452d060e365938aa9c9f7b72d70d8b26b9c60243bce227b35abd1b9df"}, + {file = "django_timezone_field-4.1.2-py3-none-any.whl", hash = "sha256:897c06e40b619cf5731a30d6c156886a7c64cba3a90364832148da7ef32ccf36"}, +] +djangorestframework = [ + {file = "djangorestframework-3.12.4-py3-none-any.whl", hash = "sha256:6d1d59f623a5ad0509fe0d6bfe93cbdfe17b8116ebc8eda86d45f6e16e819aaf"}, + {file = "djangorestframework-3.12.4.tar.gz", hash = "sha256:f747949a8ddac876e879190df194b925c177cdeb725a099db1460872f7c0a7f2"}, +] +drf-yasg = [ + {file = "drf-yasg-1.20.0.tar.gz", hash = "sha256:d50f197c7f02545d0b736df88c6d5cf874f8fea2507ad85ad7de6ae5bf2d9e5a"}, + {file = "drf_yasg-1.20.0-py2.py3-none-any.whl", hash = "sha256:8b72e5b1875931a8d11af407be3a9a5ba8776541492947a0df5bafda6b7f8267"}, +] +factory-boy = [ + {file = "factory_boy-2.12.0-py2.py3-none-any.whl", hash = "sha256:728df59b372c9588b83153facf26d3d28947fc750e8e3c95cefa9bed0e6394ee"}, + {file = "factory_boy-2.12.0.tar.gz", hash = "sha256:faf48d608a1735f0d0a3c9cbf536d64f9132b547dae7ba452c4d99a79e84a370"}, +] +faker = [ + {file = "Faker-8.1.1-py3-none-any.whl", hash = "sha256:1cc03528930fd26570077b05bd02da0625eb87b7ba192ea5c448284beb77149b"}, + {file = "Faker-8.1.1.tar.gz", hash = "sha256:14edeced0492f1516df2e8c73bd9cc02307ad89bf2b46c2c3b46b2f595ae6d24"}, +] +feedparser = [ + {file = "feedparser-5.2.1.tar.bz2", hash = "sha256:ce875495c90ebd74b179855449040003a1beb40cd13d5f037a0654251e260b02"}, + {file = "feedparser-5.2.1.tar.gz", hash = "sha256:bd030652c2d08532c034c27fcd7c85868e7fa3cb2b17f230a44a6bbc92519bf9"}, + {file = "feedparser-5.2.1.zip", hash = "sha256:cd2485472e41471632ed3029d44033ee420ad0b57111db95c240c9160a85831c"}, +] +freezegun = [ + {file = "freezegun-0.3.15-py2.py3-none-any.whl", hash = "sha256:82c757a05b7c7ca3e176bfebd7d6779fd9139c7cb4ef969c38a28d74deef89b2"}, + {file = "freezegun-0.3.15.tar.gz", hash = "sha256:e2062f2c7f95cc276a834c22f1a17179467176b624cc6f936e8bc3be5535ad1b"}, +] +ftfy = [ + {file = "ftfy-5.9.tar.gz", hash = "sha256:8c4fb2863c0b82eae2ab3cf353d9ade268dfbde863d322f78d6a9fd5cefb31e9"}, +] +gunicorn = [ + {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, +] +idna = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, + {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, +] +inflection = [ + {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, + {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, +] +isort = [ + {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, + {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, +] +itypes = [ + {file = "itypes-1.2.0-py2.py3-none-any.whl", hash = "sha256:03da6872ca89d29aef62773672b2d408f490f80db48b23079a4b194c86dd04c6"}, + {file = "itypes-1.2.0.tar.gz", hash = "sha256:af886f129dea4a2a1e3d36595a2d139589e4dd287f5cab0b40e799ee81570ff1"}, +] +jinja2 = [ + {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, + {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, +] +kombu = [ + {file = "kombu-4.6.11-py2.py3-none-any.whl", hash = "sha256:be48cdffb54a2194d93ad6533d73f69408486483d189fe9f5990ee24255b0e0a"}, + {file = "kombu-4.6.11.tar.gz", hash = "sha256:ca1b45faac8c0b18493d02a8571792f3c40291cf2bcf1f55afed3d8f3aa7ba74"}, +] +lxml = [ + {file = "lxml-4.6.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2"}, + {file = "lxml-4.6.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f"}, + {file = "lxml-4.6.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d"}, + {file = "lxml-4.6.3-cp27-cp27m-win32.whl", hash = "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106"}, + {file = "lxml-4.6.3-cp27-cp27m-win_amd64.whl", hash = "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee"}, + {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f"}, + {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4"}, + {file = "lxml-4.6.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51"}, + {file = "lxml-4.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"}, + {file = "lxml-4.6.3-cp35-cp35m-win32.whl", hash = "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2"}, + {file = "lxml-4.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4"}, + {file = "lxml-4.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4"}, + {file = "lxml-4.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3"}, + {file = "lxml-4.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d"}, + {file = "lxml-4.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec"}, + {file = "lxml-4.6.3-cp36-cp36m-win32.whl", hash = "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04"}, + {file = "lxml-4.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a"}, + {file = "lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654"}, + {file = "lxml-4.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0"}, + {file = "lxml-4.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3"}, + {file = "lxml-4.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2"}, + {file = "lxml-4.6.3-cp37-cp37m-win32.whl", hash = "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade"}, + {file = "lxml-4.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b"}, + {file = "lxml-4.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa"}, + {file = "lxml-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a"}, + {file = "lxml-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927"}, + {file = "lxml-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791"}, + {file = "lxml-4.6.3-cp38-cp38-win32.whl", hash = "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28"}, + {file = "lxml-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7"}, + {file = "lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0"}, + {file = "lxml-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1"}, + {file = "lxml-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23"}, + {file = "lxml-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969"}, + {file = "lxml-4.6.3-cp39-cp39-win32.whl", hash = "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f"}, + {file = "lxml-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83"}, + {file = "lxml-4.6.3.tar.gz", hash = "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468"}, +] +markupsafe = [ + {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] +oauthlib = [ + {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, + {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, +] +packaging = [ + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, +] +psycopg2-binary = [ + {file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-win_amd64.whl", hash = "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1"}, + {file = "psycopg2_binary-2.8.6-cp34-cp34m-win32.whl", hash = "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2"}, + {file = "psycopg2_binary-2.8.6-cp34-cp34m-win_amd64.whl", hash = "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-win32.whl", hash = "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-win_amd64.whl", hash = "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-win32.whl", hash = "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-win32.whl", hash = "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77"}, + {file = "psycopg2_binary-2.8.6-cp39-cp39-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83"}, + {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52"}, + {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd"}, + {file = "psycopg2_binary-2.8.6-cp39-cp39-win32.whl", hash = "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056"}, + {file = "psycopg2_binary-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6"}, +] +pyflakes = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +python-crontab = [ + {file = "python-crontab-2.5.1.tar.gz", hash = "sha256:4bbe7e720753a132ca4ca9d4094915f40e9d9dc8a807a4564007651018ce8c31"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +python-dotenv = [ + {file = "python-dotenv-0.12.0.tar.gz", hash = "sha256:92b3123fb2d58a284f76cc92bfe4ee6c502c32ded73e8b051c4f6afc8b6751ed"}, + {file = "python_dotenv-0.12.0-py2.py3-none-any.whl", hash = "sha256:81822227f771e0cab235a2939f0f265954ac4763cafd806d845801c863bf372f"}, +] +python-memcached = [ + {file = "python-memcached-1.59.tar.gz", hash = "sha256:a2e28637be13ee0bf1a8b6843e7490f9456fd3f2a4cb60471733c7b5d5557e4f"}, + {file = "python_memcached-1.59-py2.py3-none-any.whl", hash = "sha256:4dac64916871bd3550263323fc2ce18e1e439080a2d5670c594cf3118d99b594"}, +] +pytz = [ + {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, + {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, +] +requests = [ + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, +] +requests-oauthlib = [ + {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"}, + {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"}, + {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"}, +] +"ruamel.yaml" = [ + {file = "ruamel.yaml-0.17.4-py3-none-any.whl", hash = "sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22"}, + {file = "ruamel.yaml-0.17.4.tar.gz", hash = "sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28"}, +] +"ruamel.yaml.clib" = [ + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-win32.whl", hash = "sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-win_amd64.whl", hash = "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win32.whl", hash = "sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win32.whl", hash = "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win32.whl", hash = "sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win32.whl", hash = "sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-win32.whl", hash = "sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd"}, + {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb"}, + {file = "ruamel.yaml.clib-0.2.2.tar.gz", hash = "sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7"}, +] +sentry-sdk = [ + {file = "sentry-sdk-1.0.0.tar.gz", hash = "sha256:71de00c9711926816f750bc0f57ef2abbcb1bfbdf5378c601df7ec978f44857a"}, + {file = "sentry_sdk-1.0.0-py2.py3-none-any.whl", hash = "sha256:9221e985f425913204989d0e0e1cbb719e8b7fa10540f1bc509f660c06a34e66"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +soupsieve = [ + {file = "soupsieve-2.2.1-py3-none-any.whl", hash = "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"}, + {file = "soupsieve-2.2.1.tar.gz", hash = "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc"}, +] +sqlparse = [ + {file = "sqlparse-0.4.1-py3-none-any.whl", hash = "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0"}, + {file = "sqlparse-0.4.1.tar.gz", hash = "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"}, +] +tblib = [ + {file = "tblib-1.6.0-py2.py3-none-any.whl", hash = "sha256:e222f44485d45ed13fada73b57775e2ff9bd8af62160120bbb6679f5ad80315b"}, + {file = "tblib-1.6.0.tar.gz", hash = "sha256:229bee3754cb5d98b4837dd5c4405e80cfab57cb9f93220410ad367f8b352344"}, +] +text-unidecode = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +typing-extensions = [ + {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, + {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, + {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, +] +uritemplate = [ + {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, + {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, +] +urllib3 = [ + {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, + {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, +] +vine = [ + {file = "vine-1.3.0-py2.py3-none-any.whl", hash = "sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"}, + {file = "vine-1.3.0.tar.gz", hash = "sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +zipp = [ + {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, + {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, +] diff --git a/pyproject.toml b/pyproject.toml index e62c754..4f6dce8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,81 +1,46 @@ -[project] +[tool.poetry] name = "newsreader" -version = "0.5.3" -authors = [{ name = "Sonny" }] -license = { text = "GPL-3.0" } -requires-python = ">=3.11" -dependencies = [ - "django~=4.2", - "celery~=5.4", - "psycopg[binary]", - "django-axes", - "django-celery-beat~=2.7.0", - "django-rest-framework", - "djangorestframework-camel-case", - "pymemcache", - "python-dotenv~=1.0.1", - "ftfy~=6.2", - "requests", - "feedparser", - "bleach", - "beautifulsoup4", - "lxml", -] +version = "0.3.13.5" +description = "Webapplication for reading RSS feeds" +authors = ["Sonny "] +license = "GPL-3.0" -[dependency-groups] -test-tools = ["ruff", "factory_boy", "freezegun"] -development = [ - "django-debug-toolbar", - "django-stubs", - "django-extensions", -] -ci = ["coverage~=7.6.1"] -production = ["gunicorn~=23.0"] +[tool.poetry.dependencies] +python = "^3.7" +bleach = "^3.1.4" +Django = "^3.2" +celery = "^4.4.2" +beautifulsoup4 = "^4.9.0" +django-axes = "^5.3.1" +django-celery-beat = "^2.0.0" +djangorestframework = "^3.11.0" +drf-yasg = "^1.17.1" +django-registration-redux = "^2.7" +lxml = "^4.5.0" +feedparser = "^5.2.1" +python-memcached = "^1.59" +requests = "^2.23.0" +psycopg2-binary = "^2.8.5" +gunicorn = "^20.0.4" +python-dotenv = "^0.12.0" +sentry-sdk = {version = "^1.0.0", optional = true} +ftfy = "^5.8" +requests_oauthlib = "^1.3.0" -[project.optional-dependencies] -sentry = ["sentry-sdk~=2.0"] +[tool.poetry.extras] +sentry = ["sentry_sdk"] -[tool.uv] -environments = ["sys_platform == 'linux'"] -default-groups = ["test-tools"] +[tool.poetry.dev-dependencies] +factory-boy = "^2.12.0" +freezegun = "^0.3.15" +django-debug-toolbar = "^2.2" +django-extensions = "^2.2.9" +black = "19.3b0" +isort = "4.3.21" +autoflake = "1.3.1" +tblib = "1.6.0" +coverage = "^5.1" -[tool.ruff] -include = ["pyproject.toml", "src/**/*.py"] - -line-length = 88 - -[tool.ruff.lint] -select = ["E4", "E7", "E9", "F", "I"] - -[tool.ruff.lint.isort] -lines-between-types=1 -lines-after-imports=2 - -default-section = "third-party" -known-first-party = ["newsreader"] -section-order = [ - "future", - "standard-library", - "django", - "third-party", - "first-party", - "local-folder", -] - -[tool.ruff.lint.isort.sections] -django = ["django"] - -[tool.coverage.run] -source = ["./src/newsreader/"] -omit = [ - "**/tests/**", - "**/migrations/**", - "**/conf/**", - "**/apps.py", - "**/admin.py", - "**/tests.py", - "**/urls.py", - "**/wsgi.py", - "**/celery.py", - "**/__init__.py" -] +[build-system] +requires = ["poetry>=1.0.10"] +build-backend = "poetry.masonry.api" diff --git a/src/manage.py b/src/manage.py index dc8edeb..45fc02f 100755 --- a/src/manage.py +++ b/src/manage.py @@ -1,12 +1,11 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" - import os import sys def main(): - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.docker") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.dev") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/src/newsreader/accounts/admin.py b/src/newsreader/accounts/admin.py index 8ae55cb..02d372c 100644 --- a/src/newsreader/accounts/admin.py +++ b/src/newsreader/accounts/admin.py @@ -2,7 +2,7 @@ from django import forms from django.contrib import admin from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin from django.contrib.auth.forms import UserChangeForm -from django.utils.translation import gettext as _ +from django.utils.translation import ugettext as _ from newsreader.accounts.models import User @@ -11,6 +11,18 @@ class UserAdminForm(UserChangeForm): class Meta: widgets = { "email": forms.EmailInput(attrs={"size": "50"}), + "reddit_access_token": forms.PasswordInput( + attrs={"size": "90"}, render_value=True + ), + "reddit_refresh_token": forms.PasswordInput( + attrs={"size": "90"}, render_value=True + ), + "twitter_oauth_token": forms.PasswordInput( + attrs={"size": "90"}, render_value=True + ), + "twitter_oauth_token_secret": forms.PasswordInput( + attrs={"size": "90"}, render_value=True + ), } @@ -28,6 +40,14 @@ class UserAdmin(DjangoUserAdmin): _("User settings"), {"fields": ("email", "password", "first_name", "last_name", "is_active")}, ), + ( + _("Reddit settings"), + {"fields": ("reddit_access_token", "reddit_refresh_token")}, + ), + ( + _("Twitter settings"), + {"fields": ("twitter_oauth_token", "twitter_oauth_token_secret")}, + ), ( _("Permission settings"), {"classes": ("collapse",), "fields": ("is_staff", "is_superuser")}, diff --git a/src/newsreader/accounts/migrations/0001_initial.py b/src/newsreader/accounts/migrations/0001_initial.py index 6b22977..17b5729 100644 --- a/src/newsreader/accounts/migrations/0001_initial.py +++ b/src/newsreader/accounts/migrations/0001_initial.py @@ -8,6 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + initial = True dependencies = [ diff --git a/src/newsreader/accounts/migrations/0002_remove_user_username.py b/src/newsreader/accounts/migrations/0002_remove_user_username.py index 41ed65e..b6848a3 100644 --- a/src/newsreader/accounts/migrations/0002_remove_user_username.py +++ b/src/newsreader/accounts/migrations/0002_remove_user_username.py @@ -4,6 +4,7 @@ from django.db import migrations class Migration(migrations.Migration): + dependencies = [("accounts", "0001_initial")] operations = [migrations.RemoveField(model_name="user", name="username")] diff --git a/src/newsreader/accounts/migrations/0003_auto_20190714_1417.py b/src/newsreader/accounts/migrations/0003_auto_20190714_1417.py index 1f024cf..3d55f65 100644 --- a/src/newsreader/accounts/migrations/0003_auto_20190714_1417.py +++ b/src/newsreader/accounts/migrations/0003_auto_20190714_1417.py @@ -6,6 +6,7 @@ import newsreader.accounts.models class Migration(migrations.Migration): + dependencies = [("accounts", "0002_remove_user_username")] operations = [ diff --git a/src/newsreader/accounts/migrations/0004_auto_20190714_1501.py b/src/newsreader/accounts/migrations/0004_auto_20190714_1501.py index 6f8b5d2..69a78e3 100644 --- a/src/newsreader/accounts/migrations/0004_auto_20190714_1501.py +++ b/src/newsreader/accounts/migrations/0004_auto_20190714_1501.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0003_auto_20190714_1417")] operations = [ diff --git a/src/newsreader/accounts/migrations/0005_remove_user_task_interval.py b/src/newsreader/accounts/migrations/0005_remove_user_task_interval.py index 50746fe..262ed44 100644 --- a/src/newsreader/accounts/migrations/0005_remove_user_task_interval.py +++ b/src/newsreader/accounts/migrations/0005_remove_user_task_interval.py @@ -4,6 +4,7 @@ from django.db import migrations class Migration(migrations.Migration): + dependencies = [("accounts", "0004_auto_20190714_1501")] operations = [migrations.RemoveField(model_name="user", name="task_interval")] diff --git a/src/newsreader/accounts/migrations/0006_auto_20191116_1253.py b/src/newsreader/accounts/migrations/0006_auto_20191116_1253.py index de98048..2afd7c4 100644 --- a/src/newsreader/accounts/migrations/0006_auto_20191116_1253.py +++ b/src/newsreader/accounts/migrations/0006_auto_20191116_1253.py @@ -6,6 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0005_remove_user_task_interval")] operations = [ diff --git a/src/newsreader/accounts/migrations/0007_auto_20191116_1255.py b/src/newsreader/accounts/migrations/0007_auto_20191116_1255.py index 4425d06..eb1204a 100644 --- a/src/newsreader/accounts/migrations/0007_auto_20191116_1255.py +++ b/src/newsreader/accounts/migrations/0007_auto_20191116_1255.py @@ -6,6 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0006_auto_20191116_1253")] operations = [ diff --git a/src/newsreader/accounts/migrations/0008_auto_20200422_2243.py b/src/newsreader/accounts/migrations/0008_auto_20200422_2243.py index 98923fc..657245a 100644 --- a/src/newsreader/accounts/migrations/0008_auto_20200422_2243.py +++ b/src/newsreader/accounts/migrations/0008_auto_20200422_2243.py @@ -15,6 +15,7 @@ def update_task_name(apps, schema_editor): class Migration(migrations.Migration): + dependencies = [("accounts", "0007_auto_20191116_1255")] operations = [migrations.RunPython(update_task_name)] diff --git a/src/newsreader/accounts/migrations/0009_auto_20200524_1218.py b/src/newsreader/accounts/migrations/0009_auto_20200524_1218.py index e407f1b..3b01b0f 100644 --- a/src/newsreader/accounts/migrations/0009_auto_20200524_1218.py +++ b/src/newsreader/accounts/migrations/0009_auto_20200524_1218.py @@ -6,6 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [ ("django_celery_beat", "0012_periodictask_expire_seconds"), ("accounts", "0008_auto_20200422_2243"), diff --git a/src/newsreader/accounts/migrations/0010_auto_20200603_2230.py b/src/newsreader/accounts/migrations/0010_auto_20200603_2230.py index 4c1229e..294ff31 100644 --- a/src/newsreader/accounts/migrations/0010_auto_20200603_2230.py +++ b/src/newsreader/accounts/migrations/0010_auto_20200603_2230.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0009_auto_20200524_1218")] operations = [ diff --git a/src/newsreader/accounts/migrations/0011_auto_20200913_2101.py b/src/newsreader/accounts/migrations/0011_auto_20200913_2101.py index c2e7d7b..b6a83dd 100644 --- a/src/newsreader/accounts/migrations/0011_auto_20200913_2101.py +++ b/src/newsreader/accounts/migrations/0011_auto_20200913_2101.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0010_auto_20200603_2230")] operations = [ diff --git a/src/newsreader/accounts/migrations/0012_remove_user_task.py b/src/newsreader/accounts/migrations/0012_remove_user_task.py index 56aab08..250d300 100644 --- a/src/newsreader/accounts/migrations/0012_remove_user_task.py +++ b/src/newsreader/accounts/migrations/0012_remove_user_task.py @@ -4,6 +4,7 @@ from django.db import migrations class Migration(migrations.Migration): + dependencies = [("accounts", "0011_auto_20200913_2101")] operations = [migrations.RemoveField(model_name="user", name="task")] diff --git a/src/newsreader/accounts/migrations/0013_user_auto_mark_read.py b/src/newsreader/accounts/migrations/0013_user_auto_mark_read.py index 9d56d1c..3d975e0 100644 --- a/src/newsreader/accounts/migrations/0013_user_auto_mark_read.py +++ b/src/newsreader/accounts/migrations/0013_user_auto_mark_read.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0012_remove_user_task")] operations = [ diff --git a/src/newsreader/accounts/migrations/0014_auto_20201218_2216.py b/src/newsreader/accounts/migrations/0014_auto_20201218_2216.py index 40b7181..d809ed1 100644 --- a/src/newsreader/accounts/migrations/0014_auto_20201218_2216.py +++ b/src/newsreader/accounts/migrations/0014_auto_20201218_2216.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0013_user_auto_mark_read")] operations = [ diff --git a/src/newsreader/accounts/migrations/0015_auto_20201219_1330.py b/src/newsreader/accounts/migrations/0015_auto_20201219_1330.py index 989fe26..e32e80d 100644 --- a/src/newsreader/accounts/migrations/0015_auto_20201219_1330.py +++ b/src/newsreader/accounts/migrations/0015_auto_20201219_1330.py @@ -4,6 +4,7 @@ from django.db import migrations class Migration(migrations.Migration): + dependencies = [("accounts", "0014_auto_20201218_2216")] operations = [ diff --git a/src/newsreader/accounts/migrations/0016_alter_user_first_name.py b/src/newsreader/accounts/migrations/0016_alter_user_first_name.py index a5fade3..27ac98e 100644 --- a/src/newsreader/accounts/migrations/0016_alter_user_first_name.py +++ b/src/newsreader/accounts/migrations/0016_alter_user_first_name.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("accounts", "0015_auto_20201219_1330")] operations = [ diff --git a/src/newsreader/accounts/migrations/0017_auto_20240906_0914.py b/src/newsreader/accounts/migrations/0017_auto_20240906_0914.py deleted file mode 100644 index b3b2034..0000000 --- a/src/newsreader/accounts/migrations/0017_auto_20240906_0914.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.25 on 2024-09-06 07:14 - -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("accounts", "0016_alter_user_first_name"), - ] - - operations = [ - migrations.RemoveField( - model_name="user", - name="twitter_oauth_token", - ), - migrations.RemoveField( - model_name="user", - name="twitter_oauth_token_secret", - ), - ] diff --git a/src/newsreader/accounts/migrations/0018_remove_user_reddit_access_token_and_more.py b/src/newsreader/accounts/migrations/0018_remove_user_reddit_access_token_and_more.py deleted file mode 100644 index cf8816b..0000000 --- a/src/newsreader/accounts/migrations/0018_remove_user_reddit_access_token_and_more.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 4.2.16 on 2025-03-26 08:46 - -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("accounts", "0017_auto_20240906_0914"), - ] - - operations = [ - migrations.RemoveField( - model_name="user", - name="reddit_access_token", - ), - migrations.RemoveField( - model_name="user", - name="reddit_refresh_token", - ), - ] diff --git a/src/newsreader/accounts/models.py b/src/newsreader/accounts/models.py index 15cc97e..09da3ae 100644 --- a/src/newsreader/accounts/models.py +++ b/src/newsreader/accounts/models.py @@ -39,6 +39,14 @@ class UserManager(DjangoUserManager): class User(AbstractUser): email = models.EmailField(_("email address"), unique=True) + # reddit settings + reddit_refresh_token = models.CharField(max_length=255, blank=True, null=True) + reddit_access_token = models.CharField(max_length=255, blank=True, null=True) + + # twitter settings + twitter_oauth_token = models.CharField(max_length=255, blank=True, null=True) + twitter_oauth_token_secret = models.CharField(max_length=255, blank=True, null=True) + # settings auto_mark_read = models.BooleanField( _("Auto read marking"), @@ -60,3 +68,7 @@ class User(AbstractUser): tasks.delete() return super().delete(*args, **kwargs) + + @property + def has_twitter_auth(self): + return self.twitter_oauth_token and self.twitter_oauth_token_secret diff --git a/src/newsreader/accounts/templates/accounts/components/settings-form.html b/src/newsreader/accounts/templates/accounts/components/settings-form.html index 6e81e79..f5e7065 100644 --- a/src/newsreader/accounts/templates/accounts/components/settings-form.html +++ b/src/newsreader/accounts/templates/accounts/components/settings-form.html @@ -2,23 +2,27 @@ {% load i18n %} {% block actions %} -
-
- {% include "components/form/confirm-button.html" %} +
+
+ {% include "components/form/confirm-button.html" %} - - {% trans "Change password" %} - + + {% trans "Change password" %} + - {% if favicon_task_allowed %} - - {% trans "Fetch favicons" %} - - {% else %} - - {% endif %} -
-
+ {% if favicon_task_allowed %} + + {% trans "Fetch favicons" %} + + {% else %} + + {% endif %} + + + {% trans "Third party integrations" %} + +
+
{% endblock actions %} diff --git a/src/newsreader/accounts/templates/accounts/views/integrations.html b/src/newsreader/accounts/templates/accounts/views/integrations.html new file mode 100644 index 0000000..4429f02 --- /dev/null +++ b/src/newsreader/accounts/templates/accounts/views/integrations.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+ {% include "components/header/header.html" with title="Integrations" only %} + +
+

Reddit

+
+ {% if reddit_authorization_url %} + + {% trans "Authorize account" %} + + {% else %} + + {% endif %} + + {% if reddit_refresh_url %} + + {% trans "Refresh token" %} + + {% else %} + + {% endif %} + + {% if reddit_revoke_url %} + + {% trans "Deauthorize account" %} + + {% else %} + + {% endif %} +
+
+ +
+

Twitter

+
+ {% if twitter_auth_url %} + + {% else %} + + {% endif %} + + {% if twitter_revoke_url %} + + {% else %} + + {% endif %} +
+
+
+
+{% endblock %} diff --git a/src/newsreader/accounts/templates/accounts/views/login.html b/src/newsreader/accounts/templates/accounts/views/login.html index b83a4dd..b4c391d 100644 --- a/src/newsreader/accounts/templates/accounts/views/login.html +++ b/src/newsreader/accounts/templates/accounts/views/login.html @@ -1,9 +1,7 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% block content %} -
-
- {% include "accounts/components/login-form.html" with form=form title="Login" confirm_text="Login" %} -
+
+ {% include "accounts/components/login-form.html" with form=form title="Login" confirm_text="Login" %}
{% endblock %} diff --git a/src/newsreader/accounts/templates/accounts/views/password-change.html b/src/newsreader/accounts/templates/accounts/views/password-change.html index 1995b97..d6eb918 100644 --- a/src/newsreader/accounts/templates/accounts/views/password-change.html +++ b/src/newsreader/accounts/templates/accounts/views/password-change.html @@ -1,12 +1,8 @@ -{% extends "sidebar.html" %} - +{% extends "base.html" %} {% block content %} - {% url 'accounts:settings:home' as cancel_url %} - -
-
- {% include "components/form/form.html" with form=form title="Change password" confirm_text="Change password" cancel_url=cancel_url %} -
+
+ {% url 'accounts:settings:home' as cancel_url %} + {% include "components/form/form.html" with form=form title="Change password" confirm_text="Change password" cancel_url=cancel_url %}
{% endblock %} diff --git a/src/newsreader/accounts/templates/accounts/views/reddit.html b/src/newsreader/accounts/templates/accounts/views/reddit.html new file mode 100644 index 0000000..353ca72 --- /dev/null +++ b/src/newsreader/accounts/templates/accounts/views/reddit.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+ {% if error %} +

{% trans "Reddit authorization failed" %}

+

{{ error }}

+ {% elif access_token and refresh_token %} +

{% trans "Reddit account is linked" %}

+

{% trans "Your reddit account was successfully linked." %}

+ {% endif %} + +

+ {% trans "Return to integrations page" %} +

+
+
+{% endblock %} diff --git a/src/newsreader/accounts/templates/accounts/views/settings.html b/src/newsreader/accounts/templates/accounts/views/settings.html index 590fa0f..bf01f8e 100644 --- a/src/newsreader/accounts/templates/accounts/views/settings.html +++ b/src/newsreader/accounts/templates/accounts/views/settings.html @@ -1,9 +1,7 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% block content %} -
-
- {% include "accounts/components/settings-form.html" with form=form title="User profile" confirm_text="Save" %} -
+
+ {% include "accounts/components/settings-form.html" with form=form title="User profile" confirm_text="Save" %}
{% endblock %} diff --git a/src/newsreader/accounts/templates/accounts/views/twitter.html b/src/newsreader/accounts/templates/accounts/views/twitter.html new file mode 100644 index 0000000..6df1a97 --- /dev/null +++ b/src/newsreader/accounts/templates/accounts/views/twitter.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+ {% if error %} +

{% trans "Twitter authorization failed" %}

+

{{ error }}

+ {% elif authorized %} +

{% trans "Twitter account is linked" %}

+

{% trans "Your Twitter account was successfully linked." %}

+ {% endif %} + +

+ {% trans "Return to integrations page" %} +

+
+
+{% endblock %} diff --git a/src/newsreader/accounts/tests/factories.py b/src/newsreader/accounts/tests/factories.py index 746db80..fc13d74 100644 --- a/src/newsreader/accounts/tests/factories.py +++ b/src/newsreader/accounts/tests/factories.py @@ -5,6 +5,8 @@ from django.utils.crypto import get_random_string import factory +from registration.models import RegistrationProfile + from newsreader.accounts.models import User @@ -27,3 +29,11 @@ class UserFactory(factory.django.DjangoModelFactory): class Meta: model = User + + +class RegistrationProfileFactory(factory.django.DjangoModelFactory): + user = factory.SubFactory(UserFactory) + activation_key = factory.LazyFunction(get_activation_key) + + class Meta: + model = RegistrationProfile diff --git a/src/newsreader/accounts/tests/test_activation.py b/src/newsreader/accounts/tests/test_activation.py new file mode 100644 index 0000000..45d0909 --- /dev/null +++ b/src/newsreader/accounts/tests/test_activation.py @@ -0,0 +1,99 @@ +import datetime + +from django.conf import settings +from django.test import TestCase +from django.urls import reverse +from django.utils.translation import gettext as _ + +from registration.models import RegistrationProfile + +from newsreader.accounts.models import User + + +class ActivationTestCase(TestCase): + def setUp(self): + self.register_url = reverse("accounts:register") + self.register_success_url = reverse("accounts:register-complete") + self.success_url = reverse("accounts:activate-complete") + + def test_activation(self): + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.register_url, data) + self.assertRedirects(response, self.register_success_url) + + register_profile = RegistrationProfile.objects.get() + + kwargs = {"activation_key": register_profile.activation_key} + response = self.client.get(reverse("accounts:activate", kwargs=kwargs)) + + self.assertRedirects(response, self.success_url) + + def test_expired_key(self): + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.register_url, data) + + register_profile = RegistrationProfile.objects.get() + user = register_profile.user + + user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) + user.save() + + kwargs = {"activation_key": register_profile.activation_key} + response = self.client.get(reverse("accounts:activate", kwargs=kwargs)) + + self.assertEqual(200, response.status_code) + self.assertContains(response, _("Account activation failed")) + + user.refresh_from_db() + self.assertFalse(user.is_active) + + def test_invalid_key(self): + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.register_url, data) + self.assertRedirects(response, self.register_success_url) + + kwargs = {"activation_key": "not-a-valid-key"} + response = self.client.get(reverse("accounts:activate", kwargs=kwargs)) + + self.assertContains(response, _("Account activation failed")) + + user = User.objects.get() + + self.assertEquals(user.is_active, False) + + def test_activated_key(self): + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.register_url, data) + self.assertRedirects(response, self.register_success_url) + + register_profile = RegistrationProfile.objects.get() + + kwargs = {"activation_key": register_profile.activation_key} + response = self.client.get(reverse("accounts:activate", kwargs=kwargs)) + + self.assertRedirects(response, self.success_url) + + # try this a second time + response = self.client.get(reverse("accounts:activate", kwargs=kwargs)) + + self.assertRedirects(response, self.success_url) diff --git a/src/newsreader/accounts/tests/test_integrations.py b/src/newsreader/accounts/tests/test_integrations.py new file mode 100644 index 0000000..fbee223 --- /dev/null +++ b/src/newsreader/accounts/tests/test_integrations.py @@ -0,0 +1,537 @@ +from unittest.mock import Mock, patch +from urllib.parse import urlencode +from uuid import uuid4 + +from django.core.cache import cache +from django.test import TestCase +from django.urls import reverse +from django.utils.translation import gettext as _ + +from bs4 import BeautifulSoup + +from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.exceptions import ( + StreamException, + StreamTooManyException, +) +from newsreader.news.collection.twitter import TWITTER_AUTH_URL + + +class IntegrationsViewTestCase(TestCase): + def setUp(self): + self.user = UserFactory(email="test@test.nl", password="test") + self.client.force_login(self.user) + + self.url = reverse("accounts:settings:integrations") + + +class RedditIntegrationsTestCase(IntegrationsViewTestCase): + def test_reddit_authorization(self): + self.user.reddit_refresh_token = None + self.user.save() + + response = self.client.get(self.url) + + soup = BeautifulSoup(response.content, features="lxml") + button = soup.find("a", class_="link button button--reddit") + + self.assertEquals(button.text.strip(), "Authorize account") + + def test_reddit_refresh_token(self): + self.user.reddit_refresh_token = "jadajadajada" + self.user.reddit_access_token = None + self.user.save() + + response = self.client.get(self.url) + + soup = BeautifulSoup(response.content, features="lxml") + button = soup.find("a", class_="link button button--reddit") + + self.assertEquals(button.text.strip(), "Refresh token") + + def test_reddit_revoke(self): + self.user.reddit_refresh_token = "jadajadajada" + self.user.reddit_access_token = None + self.user.save() + + response = self.client.get(self.url) + + soup = BeautifulSoup(response.content, features="lxml") + buttons = soup.find_all("a", class_="link button button--reddit") + + self.assertIn( + "Deauthorize account", [button.text.strip() for button in buttons] + ) + + +class RedditTemplateViewTestCase(TestCase): + def setUp(self): + self.user = UserFactory(email="test@test.nl", password="test") + self.client.force_login(self.user) + + self.base_url = reverse("accounts:settings:reddit-template") + self.state = str(uuid4()) + + self.patch = patch("newsreader.news.collection.reddit.post") + self.mocked_post = self.patch.start() + + def tearDown(self): + patch.stopall() + + def test_simple(self): + response = self.client.get(self.base_url) + + self.assertEquals(response.status_code, 200) + self.assertContains(response, "Return to integrations page") + + def test_successful_authorization(self): + self.mocked_post.return_value.json.return_value = { + "access_token": "1001010412", + "refresh_token": "134510143", + } + + cache.set(f"{self.user.email}-reddit-auth", self.state) + + params = {"state": self.state, "code": "Valid code"} + url = f"{self.base_url}?{urlencode(params)}" + + response = self.client.get(url) + + self.mocked_post.assert_called_once() + + self.assertEquals(response.status_code, 200) + self.assertContains(response, "Your reddit account was successfully linked.") + + self.user.refresh_from_db() + + self.assertEquals(self.user.reddit_access_token, "1001010412") + self.assertEquals(self.user.reddit_refresh_token, "134510143") + + self.assertEquals(cache.get(f"{self.user.email}-reddit-auth"), None) + + def test_error(self): + params = {"error": "Denied authorization"} + + url = f"{self.base_url}?{urlencode(params)}" + + response = self.client.get(url) + + self.assertEquals(response.status_code, 200) + self.assertContains(response, "Denied authorization") + + def test_invalid_state(self): + cache.set(f"{self.user.email}-reddit-auth", str(uuid4())) + + params = {"code": "Valid code", "state": "Invalid state"} + + url = f"{self.base_url}?{urlencode(params)}" + + response = self.client.get(url) + + self.assertEquals(response.status_code, 200) + self.assertContains( + response, "The saved state for Reddit authorization did not match" + ) + + def test_stream_error(self): + self.mocked_post.side_effect = StreamTooManyException + + cache.set(f"{self.user.email}-reddit-auth", self.state) + + params = {"state": self.state, "code": "Valid code"} + url = f"{self.base_url}?{urlencode(params)}" + + response = self.client.get(url) + + self.mocked_post.assert_called_once() + + self.assertEquals(response.status_code, 200) + self.assertContains(response, "Too many requests") + + self.user.refresh_from_db() + + self.assertEquals(self.user.reddit_access_token, None) + self.assertEquals(self.user.reddit_refresh_token, None) + + self.assertEquals(cache.get(f"{self.user.email}-reddit-auth"), self.state) + + def test_unexpected_json(self): + self.mocked_post.return_value.json.return_value = {"message": "Happy eastern"} + + cache.set(f"{self.user.email}-reddit-auth", self.state) + + params = {"state": self.state, "code": "Valid code"} + url = f"{self.base_url}?{urlencode(params)}" + + response = self.client.get(url) + + self.mocked_post.assert_called_once() + + self.assertEquals(response.status_code, 200) + self.assertContains(response, "Access and refresh token not found in response") + + self.user.refresh_from_db() + + self.assertEquals(self.user.reddit_access_token, None) + self.assertEquals(self.user.reddit_refresh_token, None) + + self.assertEquals(cache.get(f"{self.user.email}-reddit-auth"), self.state) + + +class RedditTokenRedirectViewTestCase(TestCase): + def setUp(self): + self.user = UserFactory(email="test@test.nl", password="test") + self.client.force_login(self.user) + + self.patch = patch("newsreader.accounts.views.integrations.RedditTokenTask") + self.mocked_task = self.patch.start() + + def tearDown(self): + cache.clear() + + def test_simple(self): + response = self.client.get(reverse("accounts:settings:reddit-refresh")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.mocked_task.delay.assert_called_once_with(self.user.pk) + + self.assertEquals(1, cache.get(f"{self.user.email}-reddit-refresh")) + + def test_not_active(self): + cache.set(f"{self.user.email}-reddit-refresh", 1) + + response = self.client.get(reverse("accounts:settings:reddit-refresh")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.mocked_task.delay.assert_not_called() + + +class RedditRevokeRedirectViewTestCase(TestCase): + def setUp(self): + self.user = UserFactory(email="test@test.nl", password="test") + self.client.force_login(self.user) + + self.patch = patch("newsreader.accounts.views.integrations.revoke_reddit_token") + self.mocked_revoke = self.patch.start() + + def test_simple(self): + self.user.reddit_access_token = "jadajadajada" + self.user.reddit_refresh_token = "jadajadajada" + self.user.save() + + self.mocked_revoke.return_value = True + + response = self.client.get(reverse("accounts:settings:reddit-revoke")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.mocked_revoke.assert_called_once_with(self.user) + + self.user.refresh_from_db() + + self.assertEquals(self.user.reddit_access_token, None) + self.assertEquals(self.user.reddit_refresh_token, None) + + def test_no_refresh_token(self): + self.user.reddit_refresh_token = None + self.user.save() + + response = self.client.get(reverse("accounts:settings:reddit-revoke")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.mocked_revoke.assert_not_called() + + def test_unsuccessful_response(self): + self.user.reddit_access_token = "jadajadajada" + self.user.reddit_refresh_token = "jadajadajada" + self.user.save() + + self.mocked_revoke.return_value = False + + response = self.client.get(reverse("accounts:settings:reddit-revoke")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.user.refresh_from_db() + + self.assertEquals(self.user.reddit_access_token, "jadajadajada") + self.assertEquals(self.user.reddit_refresh_token, "jadajadajada") + + def test_stream_exception(self): + self.user.reddit_access_token = "jadajadajada" + self.user.reddit_refresh_token = "jadajadajada" + self.user.save() + + self.mocked_revoke.side_effect = StreamException + + response = self.client.get(reverse("accounts:settings:reddit-revoke")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.user.refresh_from_db() + + self.assertEquals(self.user.reddit_access_token, "jadajadajada") + self.assertEquals(self.user.reddit_refresh_token, "jadajadajada") + + +class TwitterRevokeRedirectView(TestCase): + def setUp(self): + self.user = UserFactory(email="test@test.nl", password="test") + self.client.force_login(self.user) + + self.patch = patch("newsreader.accounts.views.integrations.post") + self.mocked_post = self.patch.start() + + def tearDown(self): + patch.stopall() + + def test_simple(self): + self.user.twitter_oauth_token = "jadajadajada" + self.user.twitter_oauth_token_secret = "jadajadajada" + self.user.save() + + response = self.client.get(reverse("accounts:settings:twitter-revoke")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.user.refresh_from_db() + + self.assertIsNone(self.user.twitter_oauth_token) + self.assertIsNone(self.user.twitter_oauth_token_secret) + + def test_no_authorized_account(self): + self.user.twitter_oauth_token = None + self.user.twitter_oauth_token_secret = None + self.user.save() + + response = self.client.get(reverse("accounts:settings:twitter-revoke")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.mocked_post.assert_not_called() + + def test_stream_exception(self): + self.user.twitter_oauth_token = "jadajadajada" + self.user.twitter_oauth_token_secret = "jadajadajada" + self.user.save() + + self.mocked_post.side_effect = StreamException + + response = self.client.get(reverse("accounts:settings:twitter-revoke")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + self.user.refresh_from_db() + + self.assertEquals(self.user.twitter_oauth_token, "jadajadajada") + self.assertEquals(self.user.twitter_oauth_token_secret, "jadajadajada") + + +class TwitterAuthRedirectViewTestCase(TestCase): + def setUp(self): + self.user = UserFactory(email="test@test.nl", password="test") + self.client.force_login(self.user) + + self.patch = patch("newsreader.accounts.views.integrations.post") + self.mocked_post = self.patch.start() + + def tearDown(self): + cache.clear() + + def test_simple(self): + self.mocked_post.return_value = Mock( + text="oauth_token=foo&oauth_token_secret=bar" + ) + + response = self.client.get(reverse("accounts:settings:twitter-auth")) + + self.assertRedirects( + response, + f"{TWITTER_AUTH_URL}/?oauth_token=foo", + fetch_redirect_response=False, + ) + + cached_token = cache.get(f"twitter-{self.user.email}-token") + cached_secret = cache.get(f"twitter-{self.user.email}-secret") + + self.assertEquals(cached_token, "foo") + self.assertEquals(cached_secret, "bar") + + def test_stream_exception(self): + self.mocked_post.side_effect = StreamException + + response = self.client.get(reverse("accounts:settings:twitter-auth")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + cached_token = cache.get(f"twitter-{self.user.email}-token") + cached_secret = cache.get(f"twitter-{self.user.email}-secret") + + self.assertIsNone(cached_token) + self.assertIsNone(cached_secret) + + def test_unexpected_contents(self): + self.mocked_post.return_value = Mock(text="foo=bar&oauth_token_secret=bar") + + response = self.client.get(reverse("accounts:settings:twitter-auth")) + + self.assertRedirects(response, reverse("accounts:settings:integrations")) + + cached_token = cache.get(f"twitter-{self.user.email}-token") + cached_secret = cache.get(f"twitter-{self.user.email}-secret") + + self.assertIsNone(cached_token) + self.assertIsNone(cached_secret) + + +class TwitterTemplateViewTestCase(TestCase): + def setUp(self): + self.user = UserFactory(email="test@test.nl", password="test") + self.client.force_login(self.user) + + self.patch = patch("newsreader.accounts.views.integrations.post") + self.mocked_post = self.patch.start() + + def tearDown(self): + cache.clear() + + def test_simple(self): + cache.set_many( + { + f"twitter-{self.user.email}-token": "foo", + f"twitter-{self.user.email}-secret": "bar", + } + ) + + params = {"denied": "", "oauth_token": "foo", "oauth_verifier": "barfoo"} + + self.mocked_post.return_value = Mock( + text="oauth_token=realtoken&oauth_token_secret=realsecret" + ) + + response = self.client.get( + f"{reverse('accounts:settings:twitter-template')}?{urlencode(params)}" + ) + + self.assertContains(response, _("Twitter account is linked")) + + self.user.refresh_from_db() + + self.assertEquals(self.user.twitter_oauth_token, "realtoken") + self.assertEquals(self.user.twitter_oauth_token_secret, "realsecret") + + self.assertIsNone(cache.get(f"twitter-{self.user.email}-token")) + self.assertIsNone(cache.get(f"twitter-{self.user.email}-secret")) + + def test_denied(self): + params = {"denied": "true", "oauth_token": "foo", "oauth_verifier": "barfoo"} + + response = self.client.get( + f"{reverse('accounts:settings:twitter-template')}?{urlencode(params)}" + ) + + self.assertContains(response, _("Twitter authorization failed")) + + self.user.refresh_from_db() + + self.assertIsNone(self.user.twitter_oauth_token) + self.assertIsNone(self.user.twitter_oauth_token_secret) + + self.mocked_post.assert_not_called() + + def test_mismatched_token(self): + cache.set_many( + { + f"twitter-{self.user.email}-token": "foo", + f"twitter-{self.user.email}-secret": "bar", + } + ) + + params = {"denied": "", "oauth_token": "boo", "oauth_verifier": "barfoo"} + + response = self.client.get( + f"{reverse('accounts:settings:twitter-template')}?{urlencode(params)}" + ) + + self.assertContains(response, _("OAuth tokens failed to match")) + + self.user.refresh_from_db() + + self.assertIsNone(self.user.twitter_oauth_token) + self.assertIsNone(self.user.twitter_oauth_token_secret) + + self.mocked_post.assert_not_called() + + def test_missing_secret(self): + cache.set_many({f"twitter-{self.user.email}-token": "foo"}) + + params = {"denied": "", "oauth_token": "foo", "oauth_verifier": "barfoo"} + + response = self.client.get( + f"{reverse('accounts:settings:twitter-template')}?{urlencode(params)}" + ) + + self.assertContains(response, _("No matching tokens found for this user")) + + self.user.refresh_from_db() + + self.assertIsNone(self.user.twitter_oauth_token_secret) + + self.mocked_post.assert_not_called() + + def test_stream_exception(self): + cache.set_many( + { + f"twitter-{self.user.email}-token": "foo", + f"twitter-{self.user.email}-secret": "bar", + } + ) + + params = {"denied": "", "oauth_token": "foo", "oauth_verifier": "barfoo"} + + self.mocked_post.side_effect = StreamException + + response = self.client.get( + f"{reverse('accounts:settings:twitter-template')}?{urlencode(params)}" + ) + + self.assertContains(response, _("Failed requesting access token")) + + self.user.refresh_from_db() + + self.assertIsNone(self.user.twitter_oauth_token) + self.assertIsNone(self.user.twitter_oauth_token_secret) + + self.assertIsNotNone(cache.get(f"twitter-{self.user.email}-token")) + self.assertIsNotNone(cache.get(f"twitter-{self.user.email}-secret")) + + def test_unexpected_contents(self): + cache.set_many( + { + f"twitter-{self.user.email}-token": "foo", + f"twitter-{self.user.email}-secret": "bar", + } + ) + + params = {"denied": "", "oauth_token": "foo", "oauth_verifier": "barfoo"} + + self.mocked_post.return_value = Mock( + text="foobar=boo&oauth_token_secret=realsecret" + ) + + response = self.client.get( + f"{reverse('accounts:settings:twitter-template')}?{urlencode(params)}" + ) + + self.assertContains(response, _("No credentials found in Twitter response")) + + self.user.refresh_from_db() + + self.assertIsNone(self.user.twitter_oauth_token) + self.assertIsNone(self.user.twitter_oauth_token_secret) + + self.assertIsNotNone(cache.get(f"twitter-{self.user.email}-token")) + self.assertIsNotNone(cache.get(f"twitter-{self.user.email}-secret")) diff --git a/src/newsreader/accounts/tests/test_registration.py b/src/newsreader/accounts/tests/test_registration.py new file mode 100644 index 0000000..27c87bf --- /dev/null +++ b/src/newsreader/accounts/tests/test_registration.py @@ -0,0 +1,110 @@ +from django.core import mail +from django.test import TransactionTestCase as TestCase +from django.test.utils import override_settings +from django.urls import reverse +from django.utils.translation import gettext as _ + +from registration.models import RegistrationProfile + +from newsreader.accounts.models import User +from newsreader.accounts.tests.factories import UserFactory + + +class RegistrationTestCase(TestCase): + def setUp(self): + self.url = reverse("accounts:register") + self.success_url = reverse("accounts:register-complete") + self.disallowed_url = reverse("accounts:register-closed") + + def test_simple(self): + response = self.client.get(self.url) + + self.assertEquals(response.status_code, 200) + + def test_registration(self): + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.url, data) + self.assertRedirects(response, self.success_url) + + self.assertEquals(User.objects.count(), 1) + self.assertEquals(RegistrationProfile.objects.count(), 1) + + user = User.objects.get() + + self.assertEquals(user.is_active, False) + self.assertEquals(len(mail.outbox), 1) + + def test_existing_email(self): + UserFactory(email="test@test.com") + + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.url, data) + self.assertEquals(response.status_code, 200) + + self.assertEquals(User.objects.count(), 1) + self.assertContains(response, _("User with this Email address already exists")) + + def test_pending_registration(self): + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.url, data) + self.assertRedirects(response, self.success_url) + + self.assertEquals(User.objects.count(), 1) + self.assertEquals(RegistrationProfile.objects.count(), 1) + + user = User.objects.get() + + self.assertEquals(user.is_active, False) + self.assertEquals(len(mail.outbox), 1) + + response = self.client.post(self.url, data) + self.assertEquals(response.status_code, 200) + self.assertContains(response, _("User with this Email address already exists")) + + def test_disabled_account(self): + UserFactory(email="test@test.com", is_active=False) + + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.url, data) + self.assertEquals(response.status_code, 200) + + self.assertEquals(User.objects.count(), 1) + self.assertContains(response, _("User with this Email address already exists")) + + @override_settings(REGISTRATION_OPEN=False) + def test_registration_closed(self): + response = self.client.get(self.url) + + self.assertRedirects(response, self.disallowed_url) + + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.url, data) + self.assertRedirects(response, self.disallowed_url) + + self.assertEquals(User.objects.count(), 0) + self.assertEquals(RegistrationProfile.objects.count(), 0) diff --git a/src/newsreader/accounts/tests/test_resend_activation.py b/src/newsreader/accounts/tests/test_resend_activation.py new file mode 100644 index 0000000..0209f94 --- /dev/null +++ b/src/newsreader/accounts/tests/test_resend_activation.py @@ -0,0 +1,77 @@ +from django.core import mail +from django.test import TransactionTestCase as TestCase +from django.urls import reverse +from django.utils.translation import gettext as _ + +from registration.models import RegistrationProfile + +from newsreader.accounts.tests.factories import RegistrationProfileFactory, UserFactory + + +class ResendActivationTestCase(TestCase): + def setUp(self): + self.url = reverse("accounts:activate-resend") + self.success_url = reverse("accounts:activate-complete") + self.register_url = reverse("accounts:register") + + def test_simple(self): + response = self.client.get(self.url) + + self.assertEquals(response.status_code, 200) + + def test_resent_form(self): + data = { + "email": "test@test.com", + "password1": "test12456", + "password2": "test12456", + } + + response = self.client.post(self.register_url, data) + + register_profile = RegistrationProfile.objects.get() + original_kwargs = {"activation_key": register_profile.activation_key} + + response = self.client.post(self.url, {"email": "test@test.com"}) + + self.assertContains(response, _("We have sent an email to")) + + self.assertEquals(len(mail.outbox), 2) + + register_profile.refresh_from_db() + + kwargs = {"activation_key": register_profile.activation_key} + response = self.client.get(reverse("accounts:activate", kwargs=kwargs)) + + self.assertRedirects(response, self.success_url) + + register_profile.refresh_from_db() + user = register_profile.user + + self.assertEquals(user.is_active, True) + + # test the old activation code + response = self.client.get(reverse("accounts:activate", kwargs=original_kwargs)) + + self.assertEquals(response.status_code, 200) + self.assertContains(response, _("Account activation failed")) + + def test_existing_account(self): + user = UserFactory(is_active=True) + profile = RegistrationProfileFactory(user=user, activated=True) + + response = self.client.post(self.url, {"email": user.email}) + self.assertEquals(response.status_code, 200) + + # default behaviour is to show success page but not send an email + self.assertContains(response, _("We have sent an email to")) + + self.assertEquals(len(mail.outbox), 0) + + def test_no_account(self): + response = self.client.post(self.url, {"email": "fake@mail.com"}) + self.assertEquals(response.status_code, 200) + + # default behaviour is to show success page but not send an email + self.assertContains(response, _("We have sent an email to")) + + self.assertEquals(len(mail.outbox), 0) diff --git a/src/newsreader/accounts/tests/tests.py b/src/newsreader/accounts/tests/tests.py index 3a1ba4f..9f6a20f 100644 --- a/src/newsreader/accounts/tests/tests.py +++ b/src/newsreader/accounts/tests/tests.py @@ -15,6 +15,9 @@ class UserTestCase(TestCase): PeriodicTask.objects.create( name=f"{user.email}-feed", task="FeedTask", interval=interval ) + PeriodicTask.objects.create( + name=f"{user.email}-timeline", task="TwitterTimelineTask", interval=interval + ) user.delete() diff --git a/src/newsreader/accounts/urls.py b/src/newsreader/accounts/urls.py index 18a9b21..0eaee5c 100644 --- a/src/newsreader/accounts/urls.py +++ b/src/newsreader/accounts/urls.py @@ -2,7 +2,11 @@ from django.contrib.auth.decorators import login_required from django.urls import include, path from newsreader.accounts.views import ( + ActivationCompleteView, + ActivationResendView, + ActivationView, FaviconRedirectView, + IntegrationsView, LoginView, LogoutView, PasswordChangeView, @@ -10,11 +14,54 @@ from newsreader.accounts.views import ( PasswordResetConfirmView, PasswordResetDoneView, PasswordResetView, + RedditRevokeRedirectView, + RedditTemplateView, + RedditTokenRedirectView, + RegistrationClosedView, + RegistrationCompleteView, + RegistrationView, SettingsView, + TwitterAuthRedirectView, + TwitterRevokeRedirectView, + TwitterTemplateView, ) settings_patterns = [ + # Integrations + path( + "integrations/reddit/callback/", + login_required(RedditTemplateView.as_view()), + name="reddit-template", + ), + path( + "integrations/reddit/refresh/", + login_required(RedditTokenRedirectView.as_view()), + name="reddit-refresh", + ), + path( + "integrations/reddit/revoke/", + login_required(RedditRevokeRedirectView.as_view()), + name="reddit-revoke", + ), + path( + "integrations/twitter/auth/", + login_required(TwitterAuthRedirectView.as_view()), + name="twitter-auth", + ), + path( + "integrations/twitter/callback/", + login_required(TwitterTemplateView.as_view()), + name="twitter-template", + ), + path( + "integrations/twitter/revoke/", + login_required(TwitterRevokeRedirectView.as_view()), + name="twitter-revoke", + ), + path( + "integrations/", login_required(IntegrationsView.as_view()), name="integrations" + ), # Misc path("favicon/", login_required(FaviconRedirectView.as_view()), name="favicon"), path("", login_required(SettingsView.as_view()), name="home"), @@ -24,6 +71,24 @@ urlpatterns = [ # Auth path("login/", LoginView.as_view(), name="login"), path("logout/", LogoutView.as_view(), name="logout"), + # Register + path("register/", RegistrationView.as_view(), name="register"), + path( + "register/complete/", + RegistrationCompleteView.as_view(), + name="register-complete", + ), + path("register/closed/", RegistrationClosedView.as_view(), name="register-closed"), + path( + "activate/complete/", ActivationCompleteView.as_view(), name="activate-complete" + ), + path("activate/resend/", ActivationResendView.as_view(), name="activate-resend"), + path( + # This URL should be placed after all activate/ url's (see arg) + "activate//", + ActivationView.as_view(), + name="activate", + ), # Password path("password-reset/", PasswordResetView.as_view(), name="password-reset"), path( diff --git a/src/newsreader/accounts/views/__init__.py b/src/newsreader/accounts/views/__init__.py index d20e6bb..3be2b81 100644 --- a/src/newsreader/accounts/views/__init__.py +++ b/src/newsreader/accounts/views/__init__.py @@ -1,5 +1,14 @@ from newsreader.accounts.views.auth import LoginView, LogoutView from newsreader.accounts.views.favicon import FaviconRedirectView +from newsreader.accounts.views.integrations import ( + IntegrationsView, + RedditRevokeRedirectView, + RedditTemplateView, + RedditTokenRedirectView, + TwitterAuthRedirectView, + TwitterRevokeRedirectView, + TwitterTemplateView, +) from newsreader.accounts.views.password import ( PasswordChangeView, PasswordResetCompleteView, @@ -7,17 +16,12 @@ from newsreader.accounts.views.password import ( PasswordResetDoneView, PasswordResetView, ) +from newsreader.accounts.views.registration import ( + ActivationCompleteView, + ActivationResendView, + ActivationView, + RegistrationClosedView, + RegistrationCompleteView, + RegistrationView, +) from newsreader.accounts.views.settings import SettingsView - - -__all__ = [ - "LoginView", - "LogoutView", - "FaviconRedirectView", - "PasswordChangeView", - "PasswordResetCompleteView", - "PasswordResetConfirmView", - "PasswordResetDoneView", - "PasswordResetView", - "SettingsView", -] diff --git a/src/newsreader/accounts/views/auth.py b/src/newsreader/accounts/views/auth.py index cef1f61..0663768 100644 --- a/src/newsreader/accounts/views/auth.py +++ b/src/newsreader/accounts/views/auth.py @@ -1,10 +1,8 @@ from django.contrib.auth import views as django_views from django.urls import reverse_lazy -from newsreader.utils.views import NavListMixin - -class LoginView(NavListMixin, django_views.LoginView): +class LoginView(django_views.LoginView): template_name = "accounts/views/login.html" success_url = reverse_lazy("index") diff --git a/src/newsreader/accounts/views/integrations.py b/src/newsreader/accounts/views/integrations.py new file mode 100644 index 0000000..e6ed605 --- /dev/null +++ b/src/newsreader/accounts/views/integrations.py @@ -0,0 +1,343 @@ +import logging + +from urllib.parse import parse_qs, urlencode + +from django.conf import settings +from django.contrib import messages +from django.core.cache import cache +from django.shortcuts import redirect +from django.urls import reverse_lazy +from django.utils.translation import gettext as _ +from django.views.generic import RedirectView, TemplateView + +from requests_oauthlib import OAuth1 as OAuth + +from newsreader.news.collection.exceptions import StreamException +from newsreader.news.collection.reddit import ( + get_reddit_access_token, + get_reddit_authorization_url, + revoke_reddit_token, +) +from newsreader.news.collection.tasks import RedditTokenTask +from newsreader.news.collection.twitter import ( + TWITTER_ACCESS_TOKEN_URL, + TWITTER_AUTH_URL, + TWITTER_REQUEST_TOKEN_URL, + TWITTER_REVOKE_URL, +) +from newsreader.news.collection.utils import post + + +logger = logging.getLogger(__name__) + + +class IntegrationsView(TemplateView): + template_name = "accounts/views/integrations.html" + + def get_context_data(self, **kwargs): + return { + **super().get_context_data(**kwargs), + **self.get_reddit_context(**kwargs), + **self.get_twitter_context(**kwargs), + } + + def get_reddit_context(self, **kwargs): + user = self.request.user + reddit_authorization_url = None + reddit_refresh_url = None + + reddit_task_active = cache.get(f"{user.email}-reddit-refresh") + + if ( + user.reddit_refresh_token + and not user.reddit_access_token + and not reddit_task_active + ): + reddit_refresh_url = reverse_lazy("accounts:settings:reddit-refresh") + + if not user.reddit_refresh_token: + reddit_authorization_url = get_reddit_authorization_url(user) + + return { + "reddit_authorization_url": reddit_authorization_url, + "reddit_refresh_url": reddit_refresh_url, + "reddit_revoke_url": ( + reverse_lazy("accounts:settings:reddit-revoke") + if not reddit_authorization_url + else None + ), + } + + def get_twitter_context(self, **kwargs): + twitter_revoke_url = None + + if self.request.user.has_twitter_auth: + twitter_revoke_url = reverse_lazy("accounts:settings:twitter-revoke") + + return { + "twitter_auth_url": reverse_lazy("accounts:settings:twitter-auth"), + "twitter_revoke_url": twitter_revoke_url, + } + + +class RedditTemplateView(TemplateView): + template_name = "accounts/views/reddit.html" + + def get(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + + error = request.GET.get("error", None) + state = request.GET.get("state", None) + code = request.GET.get("code", None) + + if error: + return self.render_to_response({**context, "error": error}) + + if not code or not state: + return self.render_to_response(context) + + cached_state = cache.get(f"{request.user.email}-reddit-auth") + + if state != cached_state: + return self.render_to_response( + { + **context, + "error": _( + "The saved state for Reddit authorization did not match" + ), + } + ) + + try: + access_token, refresh_token = get_reddit_access_token(code, request.user) + + return self.render_to_response( + { + **context, + "access_token": access_token, + "refresh_token": refresh_token, + } + ) + except StreamException as e: + return self.render_to_response({**context, "error": str(e)}) + except KeyError: + return self.render_to_response( + { + **context, + "error": _("Access and refresh token not found in response"), + } + ) + + +class RedditTokenRedirectView(RedirectView): + url = reverse_lazy("accounts:settings:integrations") + + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + + user = request.user + task_active = cache.get(f"{user.email}-reddit-refresh") + + if not task_active: + RedditTokenTask.delay(user.pk) + messages.success(request, _("Access token is being retrieved")) + cache.set(f"{user.email}-reddit-refresh", 1, 300) + return response + + messages.error(request, _("Unable to retrieve token")) + return response + + +class RedditRevokeRedirectView(RedirectView): + url = reverse_lazy("accounts:settings:integrations") + + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + + user = request.user + + if not user.reddit_refresh_token: + messages.error(request, _("No reddit account is linked to this account")) + return response + + try: + is_revoked = revoke_reddit_token(user) + except StreamException: + logger.exception(f"Unable to revoke reddit token for {user.pk}") + + messages.error(request, _("Unable to revoke reddit token")) + return response + + if not is_revoked: + messages.error(request, _("Unable to revoke reddit token")) + return response + + user.reddit_access_token = None + user.reddit_refresh_token = None + user.save() + + messages.success(request, _("Reddit account deathorized")) + return response + + +class TwitterRevokeRedirectView(RedirectView): + url = reverse_lazy("accounts:settings:integrations") + + def get(self, request, *args, **kwargs): + if not request.user.has_twitter_auth: + messages.error(request, _("No twitter credentials found")) + return super().get(request, *args, **kwargs) + + oauth = OAuth( + settings.TWITTER_CONSUMER_ID, + client_secret=settings.TWITTER_CONSUMER_SECRET, + resource_owner_key=request.user.twitter_oauth_token, + resource_owner_secret=request.user.twitter_oauth_token_secret, + ) + + try: + post(TWITTER_REVOKE_URL, auth=oauth) + except StreamException: + logger.exception("Failed revoking Twitter account") + + messages.error(request, _("Unable revoke Twitter account")) + return super().get(request, *args, **kwargs) + + request.user.twitter_oauth_token = None + request.user.twitter_oauth_token_secret = None + request.user.save() + + messages.success(request, _("Twitter account revoked")) + return super().get(request, *args, **kwargs) + + +class TwitterAuthRedirectView(RedirectView): + url = reverse_lazy("accounts:settings:integrations") + + def get(self, request, *args, **kwargs): + oauth = OAuth( + settings.TWITTER_CONSUMER_ID, + client_secret=settings.TWITTER_CONSUMER_SECRET, + callback_uri=settings.TWITTER_REDIRECT_URL, + ) + + try: + response = post(TWITTER_REQUEST_TOKEN_URL, auth=oauth) + except StreamException: + logger.exception("Failed requesting Twitter authentication token") + + messages.error(request, _("Unable to retrieve initial Twitter token")) + return super().get(request, *args, **kwargs) + + params = parse_qs(response.text) + + try: + request_oauth_token = params["oauth_token"][0] + request_oauth_secret = params["oauth_token_secret"][0] + except KeyError: + logger.exception("No credentials found in response") + + messages.error(request, _("Unable to retrieve initial Twitter token")) + return super().get(request, *args, **kwargs) + + cache.set_many( + { + f"twitter-{request.user.email}-token": request_oauth_token, + f"twitter-{request.user.email}-secret": request_oauth_secret, + } + ) + + request_params = urlencode({"oauth_token": request_oauth_token}) + return redirect(f"{TWITTER_AUTH_URL}/?{request_params}") + + +class TwitterTemplateView(TemplateView): + template_name = "accounts/views/twitter.html" + + def get(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + + denied = request.GET.get("denied", False) + oauth_token = request.GET.get("oauth_token") + oauth_verifier = request.GET.get("oauth_verifier") + + if denied: + return self.render_to_response( + { + **context, + "error": _("Twitter authorization failed"), + "authorized": False, + } + ) + + cached_token = cache.get(f"twitter-{request.user.email}-token") + + if oauth_token != cached_token: + return self.render_to_response( + { + **context, + "error": _("OAuth tokens failed to match"), + "authorized": False, + } + ) + + cached_secret = cache.get(f"twitter-{request.user.email}-secret") + + if not cached_token or not cached_secret: + return self.render_to_response( + { + **context, + "error": _("No matching tokens found for this user"), + "authorized": False, + } + ) + + oauth = OAuth( + settings.TWITTER_CONSUMER_ID, + client_secret=settings.TWITTER_CONSUMER_SECRET, + resource_owner_key=cached_token, + resource_owner_secret=cached_secret, + verifier=oauth_verifier, + ) + + try: + response = post(TWITTER_ACCESS_TOKEN_URL, auth=oauth) + except StreamException: + logger.exception("Failed requesting Twitter access token") + + return self.render_to_response( + { + **context, + "error": _("Failed requesting access token"), + "authorized": False, + } + ) + + params = parse_qs(response.text) + + try: + oauth_token = params["oauth_token"][0] + oauth_secret = params["oauth_token_secret"][0] + except KeyError: + logger.exception("No credentials in Twitter response") + + return self.render_to_response( + { + **context, + "error": _("No credentials found in Twitter response"), + "authorized": False, + } + ) + + request.user.twitter_oauth_token = oauth_token + request.user.twitter_oauth_token_secret = oauth_secret + request.user.save() + + cache.delete_many( + [ + f"twitter-{request.user.email}-token", + f"twitter-{request.user.email}-secret", + ] + ) + + return self.render_to_response({**context, "error": None, "authorized": True}) diff --git a/src/newsreader/accounts/views/password.py b/src/newsreader/accounts/views/password.py index 9f792ec..e9e0aa3 100644 --- a/src/newsreader/accounts/views/password.py +++ b/src/newsreader/accounts/views/password.py @@ -1,7 +1,10 @@ from django.contrib.auth import views as django_views from django.urls import reverse_lazy -from newsreader.utils.views import NavListMixin +from newsreader.news.collection.reddit import ( + get_reddit_access_token, + get_reddit_authorization_url, +) # PasswordResetView sends the mail @@ -9,26 +12,26 @@ from newsreader.utils.views import NavListMixin # PasswordResetConfirmView checks the link the user clicked and # prompts for a new password # PasswordResetCompleteView shows a success message for the above -class PasswordResetView(NavListMixin, django_views.PasswordResetView): +class PasswordResetView(django_views.PasswordResetView): template_name = "password-reset/password-reset.html" subject_template_name = "password-reset/password-reset-subject.txt" email_template_name = "password-reset/password-reset-email.html" success_url = reverse_lazy("accounts:password-reset-done") -class PasswordResetDoneView(NavListMixin, django_views.PasswordResetDoneView): +class PasswordResetDoneView(django_views.PasswordResetDoneView): template_name = "password-reset/password-reset-done.html" -class PasswordResetConfirmView(NavListMixin, django_views.PasswordResetConfirmView): +class PasswordResetConfirmView(django_views.PasswordResetConfirmView): template_name = "password-reset/password-reset-confirm.html" success_url = reverse_lazy("accounts:password-reset-complete") -class PasswordResetCompleteView(NavListMixin, django_views.PasswordResetCompleteView): +class PasswordResetCompleteView(django_views.PasswordResetCompleteView): template_name = "password-reset/password-reset-complete.html" -class PasswordChangeView(NavListMixin, django_views.PasswordChangeView): +class PasswordChangeView(django_views.PasswordChangeView): template_name = "accounts/views/password-change.html" success_url = reverse_lazy("accounts:settings") diff --git a/src/newsreader/accounts/views/registration.py b/src/newsreader/accounts/views/registration.py new file mode 100644 index 0000000..597aa9a --- /dev/null +++ b/src/newsreader/accounts/views/registration.py @@ -0,0 +1,59 @@ +from django.shortcuts import render +from django.urls import reverse_lazy +from django.views.generic import TemplateView + +from registration.backends.default import views as registration_views + +from newsreader.news.collection.reddit import ( + get_reddit_access_token, + get_reddit_authorization_url, +) + + +# RegistrationView shows a registration form and sends the email +# RegistrationCompleteView shows after filling in the registration form +# ActivationView is send within the activation email and activates the account +# ActivationCompleteView shows the success screen when activation was succesful +# ActivationResendView can be used when activation links are expired +# RegistrationClosedView shows when registration is disabled +class RegistrationView(registration_views.RegistrationView): + disallowed_url = reverse_lazy("accounts:register-closed") + template_name = "registration/registration_form.html" + success_url = reverse_lazy("accounts:register-complete") + + +class RegistrationCompleteView(TemplateView): + template_name = "registration/registration_complete.html" + + +class RegistrationClosedView(TemplateView): + template_name = "registration/registration_closed.html" + + +# Redirects or renders failed activation template +class ActivationView(registration_views.ActivationView): + template_name = "registration/activation_failure.html" + + def get_success_url(self, user): + return ("accounts:activate-complete", (), {}) + + +class ActivationCompleteView(TemplateView): + template_name = "registration/activation_complete.html" + + +# Renders activation form resend or resend_activation_complete +class ActivationResendView(registration_views.ResendActivationView): + template_name = "registration/activation_resend_form.html" + + def render_form_submitted_template(self, form): + """ + Renders resend activation complete template with the submitted email. + + """ + email = form.cleaned_data["email"] + context = {"email": email} + + return render( + self.request, "registration/activation_resend_complete.html", context + ) diff --git a/src/newsreader/accounts/views/settings.py b/src/newsreader/accounts/views/settings.py index 7210449..aac24fb 100644 --- a/src/newsreader/accounts/views/settings.py +++ b/src/newsreader/accounts/views/settings.py @@ -4,10 +4,13 @@ from django.views.generic.edit import FormView, ModelFormMixin from newsreader.accounts.forms import UserSettingsForm from newsreader.accounts.models import User -from newsreader.utils.views import NavListMixin +from newsreader.news.collection.reddit import ( + get_reddit_access_token, + get_reddit_authorization_url, +) -class SettingsView(NavListMixin, ModelFormMixin, FormView): +class SettingsView(ModelFormMixin, FormView): template_name = "accounts/views/settings.html" success_url = reverse_lazy("accounts:settings:home") form_class = UserSettingsForm diff --git a/src/newsreader/assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf b/src/newsreader/assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf deleted file mode 100644 index 43ed4f5..0000000 Binary files a/src/newsreader/assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Inter-VariableFont_opsz,wght.ttf b/src/newsreader/assets/fonts/Inter-VariableFont_opsz,wght.ttf deleted file mode 100644 index e31b51e..0000000 Binary files a/src/newsreader/assets/fonts/Inter-VariableFont_opsz,wght.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/METADATA.pb b/src/newsreader/assets/fonts/METADATA.pb new file mode 100755 index 0000000..18857e1 --- /dev/null +++ b/src/newsreader/assets/fonts/METADATA.pb @@ -0,0 +1,101 @@ +name: "Rubik" +designer: "Hubert and Fischer, Meir Sadan, Cyreal" +license: "OFL" +category: "SANS_SERIF" +date_added: "2015-07-22" +fonts { + name: "Rubik" + style: "normal" + weight: 300 + filename: "Rubik-Light.ttf" + post_script_name: "Rubik-Light" + full_name: "Rubik Light" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "italic" + weight: 300 + filename: "Rubik-LightItalic.ttf" + post_script_name: "Rubik-LightItalic" + full_name: "Rubik Light Italic" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "normal" + weight: 400 + filename: "Rubik-Regular.ttf" + post_script_name: "Rubik-Regular" + full_name: "Rubik Regular" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "italic" + weight: 400 + filename: "Rubik-Italic.ttf" + post_script_name: "Rubik-Italic" + full_name: "Rubik Italic" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "normal" + weight: 500 + filename: "Rubik-Medium.ttf" + post_script_name: "Rubik-Medium" + full_name: "Rubik Medium" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "italic" + weight: 500 + filename: "Rubik-MediumItalic.ttf" + post_script_name: "Rubik-MediumItalic" + full_name: "Rubik Medium Italic" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "normal" + weight: 700 + filename: "Rubik-Bold.ttf" + post_script_name: "Rubik-Bold" + full_name: "Rubik Bold" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "italic" + weight: 700 + filename: "Rubik-BoldItalic.ttf" + post_script_name: "Rubik-BoldItalic" + full_name: "Rubik Bold Italic" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "normal" + weight: 900 + filename: "Rubik-Black.ttf" + post_script_name: "Rubik-Black" + full_name: "Rubik Black" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +fonts { + name: "Rubik" + style: "italic" + weight: 900 + filename: "Rubik-BlackItalic.ttf" + post_script_name: "Rubik-BlackItalic" + full_name: "Rubik Black Italic" + copyright: "Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)" +} +subsets: "cyrillic" +subsets: "cyrillic-ext" +subsets: "hebrew" +subsets: "latin" +subsets: "latin-ext" +subsets: "menu" diff --git a/src/newsreader/assets/fonts/Rubik-Black.ttf b/src/newsreader/assets/fonts/Rubik-Black.ttf new file mode 100755 index 0000000..0ffcec9 Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-Black.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-BlackItalic.ttf b/src/newsreader/assets/fonts/Rubik-BlackItalic.ttf new file mode 100755 index 0000000..5bb1d4b Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-BlackItalic.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-Bold.ttf b/src/newsreader/assets/fonts/Rubik-Bold.ttf new file mode 100755 index 0000000..5493b22 Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-Bold.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-BoldItalic.ttf b/src/newsreader/assets/fonts/Rubik-BoldItalic.ttf new file mode 100755 index 0000000..d380dac Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-BoldItalic.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-Italic.ttf b/src/newsreader/assets/fonts/Rubik-Italic.ttf new file mode 100755 index 0000000..cf43a4b Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-Italic.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-Light.ttf b/src/newsreader/assets/fonts/Rubik-Light.ttf new file mode 100755 index 0000000..f6e44cc Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-Light.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-LightItalic.ttf b/src/newsreader/assets/fonts/Rubik-LightItalic.ttf new file mode 100755 index 0000000..b9c5631 Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-LightItalic.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-Medium.ttf b/src/newsreader/assets/fonts/Rubik-Medium.ttf new file mode 100755 index 0000000..5a3f898 Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-Medium.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-MediumItalic.ttf b/src/newsreader/assets/fonts/Rubik-MediumItalic.ttf new file mode 100755 index 0000000..5b5bf1f Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-MediumItalic.ttf differ diff --git a/src/newsreader/assets/fonts/Rubik-Regular.ttf b/src/newsreader/assets/fonts/Rubik-Regular.ttf new file mode 100755 index 0000000..abdc5bc Binary files /dev/null and b/src/newsreader/assets/fonts/Rubik-Regular.ttf differ diff --git a/src/newsreader/celery.py b/src/newsreader/celery.py index fc9ae73..3eb59e0 100644 --- a/src/newsreader/celery.py +++ b/src/newsreader/celery.py @@ -3,7 +3,7 @@ import os from celery import Celery -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.docker") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.dev") app = Celery("newsreader") app.config_from_object("django.conf:settings", namespace="CELERY") diff --git a/src/newsreader/conf/base.py b/src/newsreader/conf/base.py index 220e8d1..51dfe67 100644 --- a/src/newsreader/conf/base.py +++ b/src/newsreader/conf/base.py @@ -1,25 +1,20 @@ -from dotenv import load_dotenv +import os -from newsreader.conf.utils import get_env, get_root_dir +from pathlib import Path + +from .version import get_current_version -load_dotenv() +BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent +DJANGO_PROJECT_DIR = os.path.join(BASE_DIR, "src", "newsreader") -try: - from sentry_sdk.integrations.celery import CeleryIntegration - from sentry_sdk.integrations.django import DjangoIntegration -except ImportError: - CeleryIntegration = None - DjangoIntegration = None +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ +# SECURITY WARNING: don"t run with debug turned on in production! +DEBUG = True - -BASE_DIR = get_root_dir() -DJANGO_PROJECT_DIR = BASE_DIR / "src" / "newsreader" - -DEBUG = False - -ALLOWED_HOSTS = get_env("ALLOWED_HOSTS", split=",", default=["127.0.0.1", "localhost"]) -INTERNAL_IPS = get_env("INTERNAL_IPS", split=",", default=["127.0.0.1", "localhost"]) +ALLOWED_HOSTS = ["127.0.0.1", "localhost"] +INTERNAL_IPS = ["127.0.0.1", "localhost"] # Application definition INSTALLED_APPS = [ @@ -32,8 +27,10 @@ INSTALLED_APPS = [ "django.forms", # third party apps "rest_framework", + "drf_yasg", "celery", "django_celery_beat", + "registration", "axes", # app modules "newsreader.accounts", @@ -43,8 +40,6 @@ INSTALLED_APPS = [ "newsreader.news.collection", ] -SECRET_KEY = get_env("DJANGO_SECRET_KEY", default="") - AUTHENTICATION_BACKENDS = [ "axes.backends.AxesBackend", "django.contrib.auth.backends.ModelBackend", @@ -68,10 +63,11 @@ FORM_RENDERER = "django.forms.renderers.TemplatesSetting" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [DJANGO_PROJECT_DIR / "templates"], + "DIRS": [os.path.join(DJANGO_PROJECT_DIR, "templates")], "APP_DIRS": True, "OPTIONS": { "context_processors": [ + "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", @@ -82,14 +78,15 @@ TEMPLATES = [ WSGI_APPLICATION = "newsreader.wsgi.application" +# Database +# https://docs.djangoproject.com/en/2.2/ref/settings/#databases DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", - "HOST": get_env("POSTGRES_HOST", default=""), - "PORT": get_env("POSTGRES_PORT", default=""), - "NAME": get_env("POSTGRES_DB", default=""), - "USER": get_env("POSTGRES_USER", default=""), - "PASSWORD": get_env("POSTGRES_PASSWORD", default=""), + "HOST": os.environ.get("POSTGRES_HOST", ""), + "NAME": os.environ.get("POSTGRES_NAME", "newsreader"), + "USER": os.environ.get("POSTGRES_USER"), + "PASSWORD": os.environ.get("POSTGRES_PASSWORD"), } } @@ -97,15 +94,17 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField" CACHES = { "default": { - "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", - "LOCATION": "memcached:11211", + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": "localhost:11211", }, "axes": { - "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", - "LOCATION": "memcached:11211", + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": "localhost:11211", }, } +# Logging +# https://docs.djangoproject.com/en/2.2/topics/logging/#configuring-logging LOGGING = { "version": 1, "disable_existing_loggers": False, @@ -119,46 +118,48 @@ LOGGING = { "format": "[{server_time}] {message}", "style": "{", }, + "syslog": { + "class": "logging.Formatter", + "format": "[newsreader] {message}", + "style": "{", + }, }, "handlers": { "console": { "level": "INFO", + "filters": ["require_debug_true"], "class": "logging.StreamHandler", "formatter": "timestamped", }, - "file": { - "level": "DEBUG", - "class": "logging.handlers.RotatingFileHandler", - "filename": BASE_DIR / "logs" / "newsreader.log", - "backupCount": 5, - "maxBytes": 50000000, # 50 mB - "formatter": "timestamped", - }, "celery": { "level": "INFO", - "class": "logging.handlers.RotatingFileHandler", - "filename": BASE_DIR / "logs" / "celery.log", - "backupCount": 5, - "maxBytes": 50000000, # 50 mB - "formatter": "timestamped", + "filters": ["require_debug_false"], + "class": "logging.handlers.SysLogHandler", + "formatter": "syslog", + "address": "/dev/log", + }, + "syslog": { + "level": "ERROR", + "filters": ["require_debug_false"], + "class": "logging.handlers.SysLogHandler", + "formatter": "syslog", + "address": "/dev/log", }, }, "loggers": { - "django": {"handlers": ["console"], "level": "INFO"}, + "django": {"handlers": ["console", "syslog"], "level": "INFO"}, "django.server": { - "handlers": ["console"], + "handlers": ["console", "syslog"], "level": "INFO", "propagate": False, }, - "celery.task": {"handlers": ["console", "celery"], "level": "INFO"}, - "newsreader": { - "handlers": ["console", "file"], - "level": "DEBUG", - "propagate": False, - }, + "celery": {"handlers": ["celery", "console"], "level": "INFO"}, + "newsreader": {"handlers": ["syslog", "console"], "level": "INFO"}, }, } +# Password validation +# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" @@ -173,6 +174,8 @@ AUTH_USER_MODEL = "accounts.User" LOGIN_REDIRECT_URL = "/" +# Internationalization +# https://docs.djangoproject.com/en/2.2/topics/i18n/ LANGUAGE_CODE = "en-us" TIME_ZONE = "Europe/Amsterdam" @@ -180,31 +183,37 @@ USE_I18N = True USE_L10N = True USE_TZ = True +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ STATIC_URL = "/static/" -STATIC_ROOT = BASE_DIR / "static" -STATICFILES_DIRS = (DJANGO_PROJECT_DIR / "static",) +STATIC_ROOT = os.path.join(BASE_DIR, "static") +STATICFILES_DIRS = [os.path.join(DJANGO_PROJECT_DIR, "static")] +# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-STATICFILES_FINDERS STATICFILES_FINDERS = [ "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", ] # Email -EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -DEFAULT_FROM_EMAIL = get_env( - "EMAIL_DEFAULT_FROM", required=False, default="webmaster@localhost" +# Project settings +VERSION = get_current_version() + +# Reddit integration +REDDIT_CLIENT_ID = "CLIENT_ID" +REDDIT_CLIENT_SECRET = "CLIENT_SECRET" +REDDIT_REDIRECT_URL = ( + "http://127.0.0.1:8000/accounts/settings/integrations/reddit/callback/" ) -EMAIL_HOST = get_env("EMAIL_HOST", required=False, default="localhost") -EMAIL_PORT = get_env("EMAIL_PORT", cast=int, required=False, default=25) - -EMAIL_HOST_USER = get_env("EMAIL_HOST_USER", required=False, default="") -EMAIL_HOST_PASSWORD = get_env("EMAIL_HOST_PASSWORD", required=False, default="") - -EMAIL_USE_TLS = get_env("EMAIL_USE_TLS", required=False, default=False) -EMAIL_USE_SSL = get_env("EMAIL_USE_SSL", required=False, default=False) - +# Twitter integration +TWITTER_CONSUMER_ID = "CONSUMER_ID" +TWITTER_CONSUMER_SECRET = "CONSUMER_SECRET" +TWITTER_REDIRECT_URL = ( + "http://127.0.0.1:8000/accounts/settings/integrations/twitter/callback/" +) # Third party settings AXES_HANDLER = "axes.handlers.cache.AxesCacheHandler" @@ -221,12 +230,7 @@ REST_FRAMEWORK = { "rest_framework.permissions.IsAuthenticated", "newsreader.accounts.permissions.IsOwner", ), - "DEFAULT_RENDERER_CLASSES": ( - "djangorestframework_camel_case.render.CamelCaseJSONRenderer", - ), - "DEFAULT_PARSER_CLASSES": ( - "djangorestframework_camel_case.parser.CamelCaseJSONParser", - ), + "DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",), } SWAGGER_SETTINGS = { @@ -237,15 +241,8 @@ SWAGGER_SETTINGS = { # Celery # https://docs.celeryproject.org/en/stable/userguide/configuration.html -# Note that celery settings are prefix with CELERY. See src/newsreader/celery.py. CELERY_WORKER_HIJACK_ROOT_LOGGER = False -CELERY_BROKER_URL = "amqp://guest@rabbitmq:5672" -# Sentry -SENTRY_CONFIG = { - "dsn": get_env("SENTRY_DSN", default="", required=False), - "send_default_pii": False, - "integrations": [DjangoIntegration(), CeleryIntegration()] - if DjangoIntegration and CeleryIntegration - else [], -} +REGISTRATION_OPEN = True +REGISTRATION_AUTO_LOGIN = True +ACCOUNT_ACTIVATION_DAYS = 7 diff --git a/src/newsreader/conf/ci.py b/src/newsreader/conf/ci.py deleted file mode 100644 index e69e079..0000000 --- a/src/newsreader/conf/ci.py +++ /dev/null @@ -1,46 +0,0 @@ -from .base import * # noqa: F403 -from .utils import get_current_version - - -DEBUG = True - - -del LOGGING["handlers"]["file"] # noqa: F405 -del LOGGING["handlers"]["celery"] # noqa: F405 - -LOGGING["loggers"].update( # noqa: F405 - { - "celery.task": {"handlers": ["console"], "level": "DEBUG"}, - "newsreader": {"handlers": ["console"], "level": "INFO"}, - } -) - - -EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" - -AXES_ENABLED = False - -CACHES = { - "default": { - "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", - "LOCATION": "memcached:11211", - }, - "axes": { - "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", - "LOCATION": "memcached:11211", - }, -} - -# Project settings -VERSION = get_current_version() -ENVIRONMENT = "ci" - -try: - # Optionally use sentry integration - from sentry_sdk import init as sentry_init - - SENTRY_CONFIG.update({"release": VERSION, "environment": ENVIRONMENT}) # noqa: F405 - - sentry_init(**SENTRY_CONFIG) # noqa: F405 -except ImportError: - pass diff --git a/src/newsreader/conf/dev.py b/src/newsreader/conf/dev.py index 57aaff3..8186db7 100644 --- a/src/newsreader/conf/dev.py +++ b/src/newsreader/conf/dev.py @@ -1,35 +1,19 @@ -from .base import * # noqa: F403 -from .utils import get_current_version +from .base import * # isort:skip SECRET_KEY = "mv4&5#+)-=abz3^&1r^nk_ca6y54--p(4n4cg%z*g&rb64j%wl" -MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa: F405 +MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -INSTALLED_APPS += ["debug_toolbar", "django_extensions"] # noqa: F405 - - -TEMPLATES[0]["OPTIONS"]["context_processors"].append( # noqa: F405 - "django.template.context_processors.debug", -) - -# Project settings -VERSION = get_current_version() +INSTALLED_APPS += ["debug_toolbar", "django_extensions"] # Third party settings AXES_FAILURE_LIMIT = 50 AXES_COOLOFF_TIME = None try: - # Optionally use sentry integration - from sentry_sdk import init as sentry_init - from .local import * # noqa - - SENTRY_CONFIG.update({"release": VERSION}) # noqa: F405 - - sentry_init(**SENTRY_CONFIG) # noqa: F405 except ImportError: pass diff --git a/src/newsreader/conf/docker.py b/src/newsreader/conf/docker.py index 3485bf3..f5665fa 100644 --- a/src/newsreader/conf/docker.py +++ b/src/newsreader/conf/docker.py @@ -1,43 +1,45 @@ -from .base import * # noqa: F403 -from .utils import get_current_version +from .base import * # isort:skip -DEBUG = True +SECRET_KEY = "=q(ztyo)b6noom#a164g&s9vcj1aawa^g#ing_ir99=_zl4g&$" -INSTALLED_APPS += ["debug_toolbar", "django_extensions"] # noqa: F405 +INSTALLED_APPS += ["debug_toolbar", "django_extensions"] -MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa: F405 - -LOGGING["loggers"].update( # noqa: F405 - { - "celery.task": {"handlers": ["console", "celery"], "level": "DEBUG"}, - } -) +MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": "newsreader", + "USER": "newsreader", + "PASSWORD": "newsreader", + "HOST": "db", + } +} -TEMPLATES[0]["OPTIONS"]["context_processors"].append( # noqa: F405 - "django.template.context_processors.debug", -) - -# Project settings -VERSION = get_current_version() -ENVIRONMENT = "docker" +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": "memcached:11211", + }, + "axes": { + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": "memcached:11211", + }, +} # Third party settings # Axes AXES_FAILURE_LIMIT = 50 AXES_COOLOFF_TIME = None +# Celery +# https://docs.celeryproject.org/en/latest/userguide/configuration.html +CELERY_BROKER_URL = "amqp://guest:guest@rabbitmq:5672//" + try: - # Optionally use sentry integration - from sentry_sdk import init as sentry_init - from .local import * # noqa - - SENTRY_CONFIG.update({"release": VERSION, "environment": ENVIRONMENT}) # noqa: F405 - - sentry_init(**SENTRY_CONFIG) # noqa: F405 except ImportError: pass diff --git a/src/newsreader/conf/gitlab.py b/src/newsreader/conf/gitlab.py new file mode 100644 index 0000000..67a20dd --- /dev/null +++ b/src/newsreader/conf/gitlab.py @@ -0,0 +1,19 @@ +from .base import * # isort:skip + + +SECRET_KEY = "29%lkw+&n%^w4k#@_db2mo%*tc&xzb)x7xuq*(0$eucii%4r0c" + +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" + +AXES_ENABLED = False + +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": "memcached:11211", + }, + "axes": { + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": "memcached:11211", + }, +} diff --git a/src/newsreader/conf/production.py b/src/newsreader/conf/production.py index e4053ec..b25fe09 100644 --- a/src/newsreader/conf/production.py +++ b/src/newsreader/conf/production.py @@ -1,32 +1,89 @@ -from newsreader.conf.utils import get_env +import os -from .base import * # noqa: F403 -from .utils import get_current_version +from dotenv import load_dotenv +from .base import * # isort:skip + + +load_dotenv() + DEBUG = False -SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") - +ALLOWED_HOSTS = ["rss.fudiggity.nl"] ADMINS = [ - ("", email) for email in get_env("ADMINS", split=",", required=False, default=[]) + ("", email) + for email in os.getenv("ADMINS", "").split(",") + if os.environ.get("ADMINS") ] -# Project settings -VERSION = get_current_version(debug=False) -ENVIRONMENT = "production" +SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "HOST": os.environ["POSTGRES_HOST"], + "PORT": os.environ["POSTGRES_PORT"], + "NAME": os.environ["POSTGRES_NAME"], + "USER": os.environ["POSTGRES_USER"], + "PASSWORD": os.environ["POSTGRES_PASSWORD"], + } +} + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [os.path.join(DJANGO_PROJECT_DIR, "templates")], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ] + }, + } +] + +# Email +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +DEFAULT_FROM_EMAIL = os.environ.get("EMAIL_DEFAULT_FROM", "webmaster@localhost") + +EMAIL_HOST = os.environ.get("EMAIL_HOST", "localhost") +EMAIL_PORT = os.environ.get("EMAIL_PORT", 25) + +EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER", "") +EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", "") + +EMAIL_USE_TLS = bool(os.environ.get("EMAIL_USE_TLS")) +EMAIL_USE_SSL = bool(os.environ.get("EMAIL_USE_SSL")) + +# Reddit integration +REDDIT_CLIENT_ID = os.environ.get("REDDIT_CLIENT_ID", "") +REDDIT_CLIENT_SECRET = os.environ.get("REDDIT_CLIENT_SECRET", "") +REDDIT_REDIRECT_URL = os.environ.get("REDDIT_CALLBACK_URL", "") + +# Twitter integration +TWITTER_CONSUMER_ID = os.environ.get("TWITTER_CONSUMER_ID", "") +TWITTER_CONSUMER_SECRET = os.environ.get("TWITTER_CONSUMER_SECRET", "") +TWITTER_REDIRECT_URL = os.environ.get("TWITTER_REDIRECT_URL", "") # Third party settings AXES_HANDLER = "axes.handlers.database.AxesDatabaseHandler" +REGISTRATION_OPEN = False + # Optionally use sentry integration try: from sentry_sdk import init as sentry_init + from sentry_sdk.integrations.celery import CeleryIntegration + from sentry_sdk.integrations.django import DjangoIntegration - SENTRY_CONFIG.update( # noqa: F405 - {"release": VERSION, "environment": ENVIRONMENT, "debug": False} + sentry_init( + dsn=os.environ.get("SENTRY_DSN"), + integrations=[DjangoIntegration(), CeleryIntegration()], + send_default_pii=False, + release=VERSION, ) - - sentry_init(**SENTRY_CONFIG) # noqa: F405 except ImportError: pass diff --git a/src/newsreader/conf/utils.py b/src/newsreader/conf/utils.py deleted file mode 100644 index c46b59d..0000000 --- a/src/newsreader/conf/utils.py +++ /dev/null @@ -1,85 +0,0 @@ -import logging -import os -import subprocess - -from pathlib import Path -from typing import Any, Iterable, Type - - -logger = logging.getLogger(__name__) - - -def get_env( - name: str, - cast: Type = str, - required: bool = True, - default: Any = None, - split: str = "", -) -> Any: - if cast is not str and split: - raise TypeError(f"Split is not possible with {cast}") - - value = os.getenv(name) - - if not value: - if required: - logger.warning(f"Missing environment variable: {name}") - - return default - - bool_mapping = {"yes": True, "true": True, "false": False, "no": False} - - if cast is bool: - _value = bool_mapping.get(value.lower()) - - if not value: - raise ValueError(f"Unknown boolean value: {_value}") - - return _value - - value = value if not cast else cast(value) - return value if not split else value.split(split) - - -def get_current_version(debug: bool = True) -> str: - version = get_env("VERSION", required=False) - - if version: - return version - - if debug: - try: - output = subprocess.check_output( - ["git", "show", "--no-patch", "--format=%H"], universal_newlines=True - ) - return output.strip() - except (subprocess.CalledProcessError, OSError): - return "" - - try: - output = subprocess.check_output( - ["git", "describe", "--tags"], universal_newlines=True - ) - return output.strip() - except (subprocess.CalledProcessError, OSError): - return "" - - -ROOT_MARKERS = ("pyproject.toml", "package.json", "README.md", "CHANGELOG.md") - - -def get_root_dir() -> Path: - file = Path(__file__) - return _traverse_dirs(file.parent, ROOT_MARKERS) - - -def _traverse_dirs(path: Path, root_markers: Iterable[str]) -> Path: - if path.parent == path: - raise OSError("Root directory detected") - - files = (file.name for file in path.iterdir()) - - if not any((marker for marker in root_markers if marker in files)): - return _traverse_dirs(path.parent, root_markers) - - return path diff --git a/src/newsreader/conf/version.py b/src/newsreader/conf/version.py new file mode 100644 index 0000000..f8b4c8d --- /dev/null +++ b/src/newsreader/conf/version.py @@ -0,0 +1,15 @@ +import os +import subprocess + + +def get_current_version(): + if "VERSION" in os.environ: + return os.environ["VERSION"] + + try: + output = subprocess.check_output( + ["git", "describe", "--tags"], universal_newlines=True + ) + return output.strip() + except (subprocess.CalledProcessError, OSError): + return "" diff --git a/src/newsreader/fixtures/default-fixture.json b/src/newsreader/fixtures/default-fixture.json new file mode 100644 index 0000000..880db4c --- /dev/null +++ b/src/newsreader/fixtures/default-fixture.json @@ -0,0 +1,4022 @@ +[ +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "admin", + "model": "logentry" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "auth", + "model": "permission" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "auth", + "model": "group" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "contenttypes", + "model": "contenttype" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "sessions", + "model": "session" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "django_celery_beat", + "model": "crontabschedule" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "django_celery_beat", + "model": "intervalschedule" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "django_celery_beat", + "model": "periodictask" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "django_celery_beat", + "model": "periodictasks" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "django_celery_beat", + "model": "solarschedule" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "django_celery_beat", + "model": "clockedschedule" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "registration", + "model": "registrationprofile" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "registration", + "model": "supervisedregistrationprofile" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "axes", + "model": "accessattempt" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "axes", + "model": "accesslog" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "accounts", + "model": "user" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "core", + "model": "post" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "core", + "model": "category" + } +}, +{ + "model": "contenttypes.contenttype", + "fields": { + "app_label": "collection", + "model": "collectionrule" + } +}, +{ + "model": "sessions.session", + "pk": "3sumq22krk8tsvexcs4b8czu82yhvuer", + "fields": { + "session_data": "OWZkZTQyZDQ2NzNkYzdkOTBhM2ZlOWU3MDhhNDkyMWQ0MDdmZTc5ODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiJhZTMwMWFlMzI5OGFlOThkNjY1MTY1NDIxM2EyMmM0NDA0Y2FkZTc3In0=", + "expire_date": "2020-05-16T18:29:04.049Z" + } +}, +{ + "model": "sessions.session", + "pk": "8ix6bdwf2ywk0eir1hb062dhfh9xit85", + "fields": { + "session_data": "OWZkZTQyZDQ2NzNkYzdkOTBhM2ZlOWU3MDhhNDkyMWQ0MDdmZTc5ODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiJhZTMwMWFlMzI5OGFlOThkNjY1MTY1NDIxM2EyMmM0NDA0Y2FkZTc3In0=", + "expire_date": "2020-07-21T19:36:54.530Z" + } +}, +{ + "model": "sessions.session", + "pk": "d4wophwpjm8z96doe8iddvhdv9yfafyx", + "fields": { + "session_data": "OWZkZTQyZDQ2NzNkYzdkOTBhM2ZlOWU3MDhhNDkyMWQ0MDdmZTc5ODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiJhZTMwMWFlMzI5OGFlOThkNjY1MTY1NDIxM2EyMmM0NDA0Y2FkZTc3In0=", + "expire_date": "2020-06-07T19:45:49.727Z" + } +}, +{ + "model": "sessions.session", + "pk": "g23ziz66li5zx8nd8cewb3vevdxhjkm0", + "fields": { + "session_data": "OWZkZTQyZDQ2NzNkYzdkOTBhM2ZlOWU3MDhhNDkyMWQ0MDdmZTc5ODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiJhZTMwMWFlMzI5OGFlOThkNjY1MTY1NDIxM2EyMmM0NDA0Y2FkZTc3In0=", + "expire_date": "2020-06-30T06:55:50.747Z" + } +}, +{ + "model": "sessions.session", + "pk": "jwn66dptmdkm6hom2ns3j288aaxqtyjd", + "fields": { + "session_data": "OWZkZTQyZDQ2NzNkYzdkOTBhM2ZlOWU3MDhhNDkyMWQ0MDdmZTc5ODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiJhZTMwMWFlMzI5OGFlOThkNjY1MTY1NDIxM2EyMmM0NDA0Y2FkZTc3In0=", + "expire_date": "2020-06-07T18:38:19.116Z" + } +}, +{ + "model": "sessions.session", + "pk": "wjz6kwg5e5ciemre0l0wwyrcwcj2gyg6", + "fields": { + "session_data": "MWU5ODBjY2QyOTFhMmRiY2QyYjQwZjQ3MmMwYmExYjBlYTkxNTcwODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiI0YWZkYTkxNzU5ZDBhZDZmMjg1ZTQyOGY0OTUxN2M5MTJhMmM5NWIyIn0=", + "expire_date": "2020-08-09T09:52:04.705Z" + } +}, +{ + "model": "django_celery_beat.intervalschedule", + "pk": 1, + "fields": { + "every": 5, + "period": "minutes" + } +}, +{ + "model": "django_celery_beat.intervalschedule", + "pk": 2, + "fields": { + "every": 15, + "period": "minutes" + } +}, +{ + "model": "django_celery_beat.intervalschedule", + "pk": 3, + "fields": { + "every": 30, + "period": "minutes" + } +}, +{ + "model": "django_celery_beat.intervalschedule", + "pk": 4, + "fields": { + "every": 1, + "period": "hours" + } +}, +{ + "model": "django_celery_beat.intervalschedule", + "pk": 5, + "fields": { + "every": 4, + "period": "hours" + } +}, +{ + "model": "django_celery_beat.crontabschedule", + "pk": 1, + "fields": { + "minute": "0", + "hour": "4", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + "timezone": "UTC" + } +}, +{ + "model": "django_celery_beat.periodictasks", + "pk": 1, + "fields": { + "last_update": "2020-07-26T09:47:48.298Z" + } +}, +{ + "model": "django_celery_beat.periodictask", + "pk": 1, + "fields": { + "name": "celery.backend_cleanup", + "task": "celery.backend_cleanup", + "interval": null, + "crontab": 1, + "solar": null, + "clocked": null, + "args": "[]", + "kwargs": "{}", + "queue": null, + "exchange": null, + "routing_key": null, + "headers": "{}", + "priority": null, + "expires": null, + "expire_seconds": 43200, + "one_off": false, + "start_time": null, + "enabled": true, + "last_run_at": "2020-07-26T09:47:48.322Z", + "total_run_count": 17, + "date_changed": "2020-07-26T09:47:50.362Z", + "description": "" + } +}, +{ + "model": "django_celery_beat.periodictask", + "pk": 10, + "fields": { + "name": "sonny@bakker.nl-collection-task", + "task": "FeedTask", + "interval": 5, + "crontab": null, + "solar": null, + "clocked": null, + "args": "[1]", + "kwargs": "{}", + "queue": null, + "exchange": null, + "routing_key": null, + "headers": "{}", + "priority": null, + "expires": null, + "expire_seconds": null, + "one_off": false, + "start_time": null, + "enabled": false, + "last_run_at": "2020-07-14T11:45:26.209Z", + "total_run_count": 307, + "date_changed": "2020-07-14T11:45:41.282Z", + "description": "" + } +}, +{ + "model": "django_celery_beat.periodictask", + "pk": 11, + "fields": { + "name": "Reddit collection task", + "task": "RedditTask", + "interval": 5, + "crontab": null, + "solar": null, + "clocked": null, + "args": "[]", + "kwargs": "{}", + "queue": null, + "exchange": null, + "routing_key": null, + "headers": "{}", + "priority": null, + "expires": null, + "expire_seconds": null, + "one_off": false, + "start_time": null, + "enabled": false, + "last_run_at": null, + "total_run_count": 4, + "date_changed": "2020-07-14T11:45:41.316Z", + "description": "" + } +}, +{ + "model": "core.post", + "pk": 3061, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-21T20:14:50.423Z", + "title": "Star Citizen: Question and Answer Thread", + "body": "

Welcome to the Star Citizen question and answer thread. Feel free to ask any questions you have related to SC here!

\n\n\n\n

Useful Links and Resources:

\n\n

Star Citizen Wiki - The biggest and best wiki resource dedicated to Star Citizen

\n\n

Star Citizen FAQ - Chances the answer you need is here.

\n\n

Discord Help Channel - Often times community members will be here to help you with issues.

\n\n

Referral Code Randomizer - Use this when creating a new account to get 5000 extra UEC.

\n\n

Download Star Citizen - Get the latest version of Star Citizen here

\n\n

Current Game Features - Click here to see what you can currently do in Star Citizen.

\n\n

Development Roadmap - The current development status of up and coming Star Citizen features.

\n\n

Pledge FAQ - Official FAQ regarding spending money on the game.

\n
", + "author": "UEE_Central_Computer", + "publication_date": "2020-07-20T14:00:10Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huk04t/star_citizen_question_and_answer_thread/", + "read": false, + "rule": 82, + "remote_identifier": "huk04t" + } +}, +{ + "model": "core.post", + "pk": 3062, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:33:37.019Z", + "title": "Peace and Quiet", + "body": "
\"Peace
", + "author": "SourMemeNZ", + "publication_date": "2020-07-20T14:09:49Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huk4ib/peace_and_quiet/", + "read": true, + "rule": 82, + "remote_identifier": "huk4ib" + } +}, +{ + "model": "core.post", + "pk": 3063, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-21T20:14:50.463Z", + "title": "Y'all are probably sick of em by now but here's my LEGO Mercury Star Runner (MSR).", + "body": "
\"Y'all
", + "author": "osamadabinman", + "publication_date": "2020-07-20T19:53:23Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hupzqa/yall_are_probably_sick_of_em_by_now_but_heres_my/", + "read": true, + "rule": 82, + "remote_identifier": "hupzqa" + } +}, +{ + "model": "core.post", + "pk": 3064, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-21T20:17:12.253Z", + "title": "Damned Space Invaders and their pixel weapons!", + "body": "
\"Damned
", + "author": "Akaradrin", + "publication_date": "2020-07-20T14:26:18Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hukckf/damned_space_invaders_and_their_pixel_weapons/", + "read": true, + "rule": 82, + "remote_identifier": "hukckf" + } +}, +{ + "model": "core.post", + "pk": 3065, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:32:35.578Z", + "title": "The sky is no longer the limit", + "body": "
\"The
", + "author": "CyberTill", + "publication_date": "2020-07-20T14:11:31Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huk5b8/the_sky_is_no_longer_the_limit/", + "read": false, + "rule": 82, + "remote_identifier": "huk5b8" + } +}, +{ + "model": "core.post", + "pk": 3066, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-21T20:17:23.282Z", + "title": "Terrapin Hover Mode Gameplay [Full Video in Comments]", + "body": "
", + "author": "Didactic_Tomato", + "publication_date": "2020-07-20T11:01:13Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hui1gv/terrapin_hover_mode_gameplay_full_video_in/", + "read": true, + "rule": 82, + "remote_identifier": "hui1gv" + } +}, +{ + "model": "core.post", + "pk": 3067, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-21T20:17:44.250Z", + "title": "honestly", + "body": "
\"honestly\"
", + "author": "Beatlead", + "publication_date": "2020-07-20T18:24:07Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huo96t/honestly/", + "read": true, + "rule": 82, + "remote_identifier": "huo96t" + } +}, +{ + "model": "core.post", + "pk": 3068, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:32:35.584Z", + "title": "As a paranoiac and tired of checking if door was closed, saved to f4 thoses \"security cam\" positions, could be usefull for larger ships :)", + "body": "", + "author": "icwiener__", + "publication_date": "2020-07-20T13:03:33Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hujchz/as_a_paranoiac_and_tired_of_checking_if_door_was/", + "read": false, + "rule": 82, + "remote_identifier": "hujchz" + } +}, +{ + "model": "core.post", + "pk": 3069, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:33:59.158Z", + "title": "Station Manager: \"You're too fat, we won't let you in, go and fall on Lorville. Thank you for your call!\" Me: \"okay :'(\"", + "body": "
\"Station
", + "author": "Shaman_N_One", + "publication_date": "2020-07-20T11:33:38Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huidlu/station_manager_youre_too_fat_we_wont_let_you_in/", + "read": true, + "rule": 82, + "remote_identifier": "huidlu" + } +}, +{ + "model": "core.post", + "pk": 3070, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:32:35.588Z", + "title": "[PTU Bug Hunt Request] Packet Loss", + "body": "", + "author": "Rainwalker007", + "publication_date": "2020-07-20T18:38:03Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huoicq/ptu_bug_hunt_request_packet_loss/", + "read": false, + "rule": 82, + "remote_identifier": "huoicq" + } +}, +{ + "model": "core.post", + "pk": 3071, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-21T20:17:52.092Z", + "title": "Anyone able to explain these \"trail frames\"?", + "body": "
\"Anyone
", + "author": "Abnormal_Sloth", + "publication_date": "2020-07-20T17:11:32Z", + "url": "https://www.reddit.com/r/starcitizen/comments/humyeq/anyone_able_to_explain_these_trail_frames/", + "read": true, + "rule": 82, + "remote_identifier": "humyeq" + } +}, +{ + "model": "core.post", + "pk": 3072, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:32:35.593Z", + "title": "#BringBackBugSmasher - A long forgotten legendary video content", + "body": "", + "author": "MasterBoring", + "publication_date": "2020-07-20T18:05:54Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hunx77/bringbackbugsmasher_a_long_forgotten_legendary/", + "read": false, + "rule": 82, + "remote_identifier": "hunx77" + } +}, +{ + "model": "core.post", + "pk": 3073, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:33:22.601Z", + "title": "Oracle Helmet [in-game screenshot; downsampled to 4k]", + "body": "
\"Oracle
", + "author": "mr-hasgaha", + "publication_date": "2020-07-20T17:39:34Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hung0b/oracle_helmet_ingame_screenshot_downsampled_to_4k/", + "read": true, + "rule": 82, + "remote_identifier": "hung0b" + } +}, +{ + "model": "core.post", + "pk": 3074, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:34:42.578Z", + "title": "Testing 3.10 - Gladius in decoupled mode", + "body": "
", + "author": "DarkConstant", + "publication_date": "2020-07-19T21:26:52Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hu6f1h/testing_310_gladius_in_decoupled_mode/", + "read": true, + "rule": 82, + "remote_identifier": "hu6f1h" + } +}, +{ + "model": "core.post", + "pk": 3075, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:34:29.424Z", + "title": "Day 3, I can't stop taking pictures with my Carrack. Send help", + "body": "
\"Day
", + "author": "CyberTill", + "publication_date": "2020-07-20T01:58:15Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huazyy/day_3_i_cant_stop_taking_pictures_with_my_carrack/", + "read": true, + "rule": 82, + "remote_identifier": "huazyy" + } +}, +{ + "model": "core.post", + "pk": 3076, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:32:35.602Z", + "title": "I used to enjoy flying between the buildings of new babbage, I mean before the NFZ \"improvement\"", + "body": "
\"I
", + "author": "shoeii", + "publication_date": "2020-07-20T16:40:26Z", + "url": "https://www.reddit.com/r/starcitizen/comments/humet2/i_used_to_enjoy_flying_between_the_buildings_of/", + "read": false, + "rule": 82, + "remote_identifier": "humet2" + } +}, +{ + "model": "core.post", + "pk": 3077, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-21T20:18:04.237Z", + "title": "Thank you CIG for updated heightmaps and render distances", + "body": "
\"Thank
", + "author": "u7f76", + "publication_date": "2020-07-19T23:38:22Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hu8pwf/thank_you_cig_for_updated_heightmaps_and_render/", + "read": true, + "rule": 82, + "remote_identifier": "hu8pwf" + } +}, +{ + "model": "core.post", + "pk": 3078, + "fields": { + "created": "2020-07-20T19:32:35.562Z", + "modified": "2020-07-20T19:32:35.607Z", + "title": "This Week in Star Citizen | July 20th 2020", + "body": "", + "author": "ivtiprogamer", + "publication_date": "2020-07-20T19:50:29Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hupxnt/this_week_in_star_citizen_july_20th_2020/", + "read": false, + "rule": 82, + "remote_identifier": "hupxnt" + } +}, +{ + "model": "core.post", + "pk": 3079, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:34:36.068Z", + "title": "Bravo CIG lighting team! Noticeable improvements to all around environment lighting in 3.10", + "body": "
\"Bravo
", + "author": "u7f76", + "publication_date": "2020-07-20T00:02:23Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hu94o0/bravo_cig_lighting_team_noticeable_improvements/", + "read": true, + "rule": 82, + "remote_identifier": "hu94o0" + } +}, +{ + "model": "core.post", + "pk": 3080, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:32:35.613Z", + "title": "Thick", + "body": "
\"Thick\"
", + "author": "burgerbagel", + "publication_date": "2020-07-20T16:24:38Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hum50f/thick/", + "read": false, + "rule": 82, + "remote_identifier": "hum50f" + } +}, +{ + "model": "core.post", + "pk": 3081, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:34:19.763Z", + "title": "Soon\u2122", + "body": "
\"Soon\u2122\"
", + "author": "Mistralette", + "publication_date": "2020-07-20T05:54:09Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hueg01/soon/", + "read": true, + "rule": 82, + "remote_identifier": "hueg01" + } +}, +{ + "model": "core.post", + "pk": 3082, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:32:35.618Z", + "title": "On the prowl", + "body": "
\"On
", + "author": "SaraCaterina", + "publication_date": "2020-07-20T16:37:03Z", + "url": "https://www.reddit.com/r/starcitizen/comments/humcmb/on_the_prowl/", + "read": false, + "rule": 82, + "remote_identifier": "humcmb" + } +}, +{ + "model": "core.post", + "pk": 3083, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:34:07.272Z", + "title": "The Hills Have Eyes", + "body": "
\"The
", + "author": "FallenLordik", + "publication_date": "2020-07-20T11:19:19Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hui8ao/the_hills_have_eyes/", + "read": true, + "rule": 82, + "remote_identifier": "hui8ao" + } +}, +{ + "model": "core.post", + "pk": 3084, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:32:35.623Z", + "title": "Worried about longer loading screens? Hit ~ and do r_displayinfo 3", + "body": "
\"Worried
", + "author": "kristokn", + "publication_date": "2020-07-20T10:09:53Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huhif1/worried_about_longer_loading_screens_hit_and_do_r/", + "read": false, + "rule": 82, + "remote_identifier": "huhif1" + } +}, +{ + "model": "core.post", + "pk": 3085, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:32:35.625Z", + "title": "My contribution to the wallpaper contest... click for the full effect (3440x1440)", + "body": "
\"My
", + "author": "Dougie_Juice", + "publication_date": "2020-07-20T20:02:31Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huq655/my_contribution_to_the_wallpaper_contest_click/", + "read": false, + "rule": 82, + "remote_identifier": "huq655" + } +}, +{ + "model": "core.post", + "pk": 3086, + "fields": { + "created": "2020-07-20T19:32:35.563Z", + "modified": "2020-07-20T19:32:35.627Z", + "title": "Star Citizen: The Onion (Parody Project)", + "body": "", + "author": "BroadOne", + "publication_date": "2020-07-20T19:19:20Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hupbkj/star_citizen_the_onion_parody_project/", + "read": false, + "rule": 82, + "remote_identifier": "hupbkj" + } +}, +{ + "model": "core.post", + "pk": 3087, + "fields": { + "created": "2020-07-20T19:32:35.635Z", + "modified": "2020-07-20T19:32:35.637Z", + "title": "perfect day to sunbathe", + "body": "
", + "author": "Pedrica1", + "publication_date": "2020-07-20T18:08:17Z", + "url": "https://www.reddit.com/r/aww/comments/hunysb/perfect_day_to_sunbathe/", + "read": false, + "rule": 81, + "remote_identifier": "hunysb" + } +}, +{ + "model": "core.post", + "pk": 3088, + "fields": { + "created": "2020-07-20T19:32:35.635Z", + "modified": "2020-07-20T19:32:35.639Z", + "title": "My dogs face when he sees I'm home", + "body": "
", + "author": "NewReddit_WhoDis", + "publication_date": "2020-07-20T16:45:21Z", + "url": "https://www.reddit.com/r/aww/comments/humhxa/my_dogs_face_when_he_sees_im_home/", + "read": false, + "rule": 81, + "remote_identifier": "humhxa" + } +}, +{ + "model": "core.post", + "pk": 3089, + "fields": { + "created": "2020-07-20T19:32:35.635Z", + "modified": "2020-07-20T19:32:35.641Z", + "title": "Cow loves the scritch machine", + "body": "
", + "author": "Der_Ist", + "publication_date": "2020-07-20T17:36:16Z", + "url": "https://www.reddit.com/r/aww/comments/hundvo/cow_loves_the_scritch_machine/", + "read": false, + "rule": 81, + "remote_identifier": "hundvo" + } +}, +{ + "model": "core.post", + "pk": 3090, + "fields": { + "created": "2020-07-20T19:32:35.635Z", + "modified": "2020-07-20T19:32:35.643Z", + "title": "Can I sit next to you ?", + "body": "
", + "author": "wheezy098", + "publication_date": "2020-07-20T17:55:10Z", + "url": "https://www.reddit.com/r/aww/comments/hunq5h/can_i_sit_next_to_you/", + "read": false, + "rule": 81, + "remote_identifier": "hunq5h" + } +}, +{ + "model": "core.post", + "pk": 3091, + "fields": { + "created": "2020-07-20T19:32:35.635Z", + "modified": "2020-07-20T19:32:35.645Z", + "title": "IS THAT A CUSTOMER? flop flop flop flop .... \" Can I uhh... help you sir?\"", + "body": "
", + "author": "MBMV", + "publication_date": "2020-07-20T12:50:40Z", + "url": "https://www.reddit.com/r/aww/comments/huj7g3/is_that_a_customer_flop_flop_flop_flop_can_i_uhh/", + "read": false, + "rule": 81, + "remote_identifier": "huj7g3" + } +}, +{ + "model": "core.post", + "pk": 3092, + "fields": { + "created": "2020-07-20T19:32:35.635Z", + "modified": "2020-07-20T19:32:35.647Z", + "title": "Good Boy turned Disney Princess", + "body": "
", + "author": "Sauwercraud", + "publication_date": "2020-07-20T18:40:05Z", + "url": "https://www.reddit.com/r/aww/comments/huojq0/good_boy_turned_disney_princess/", + "read": false, + "rule": 81, + "remote_identifier": "huojq0" + } +}, +{ + "model": "core.post", + "pk": 3093, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.649Z", + "title": "Kitty loop", + "body": "
", + "author": "Dlatrex", + "publication_date": "2020-07-20T12:54:02Z", + "url": "https://www.reddit.com/r/aww/comments/huj8s6/kitty_loop/", + "read": false, + "rule": 81, + "remote_identifier": "huj8s6" + } +}, +{ + "model": "core.post", + "pk": 3094, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.652Z", + "title": "if i fits i sits", + "body": "
", + "author": "jasontaken", + "publication_date": "2020-07-20T16:38:32Z", + "url": "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/", + "read": false, + "rule": 81, + "remote_identifier": "humdlf" + } +}, +{ + "model": "core.post", + "pk": 3095, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.654Z", + "title": "Isn\u2019t she Adorable !", + "body": "
\"Isn\u2019t
", + "author": "MunchyMac", + "publication_date": "2020-07-20T16:18:05Z", + "url": "https://www.reddit.com/r/aww/comments/hum133/isnt_she_adorable/", + "read": false, + "rule": 81, + "remote_identifier": "hum133" + } +}, +{ + "model": "core.post", + "pk": 3096, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.655Z", + "title": "Thank you mama (\u2283\uff61\u2022\u0301\u203f\u2022\u0300\uff61)\u2283", + "body": "
", + "author": "AnoushkaSingh", + "publication_date": "2020-07-20T13:35:51Z", + "url": "https://www.reddit.com/r/aww/comments/hujpxy/thank_you_mama/", + "read": false, + "rule": 81, + "remote_identifier": "hujpxy" + } +}, +{ + "model": "core.post", + "pk": 3097, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.657Z", + "title": "I WANT TO HUG HIM SO BAD!!!", + "body": "
", + "author": "BATMAN_5777", + "publication_date": "2020-07-20T18:25:20Z", + "url": "https://www.reddit.com/r/aww/comments/huo9z4/i_want_to_hug_him_so_bad/", + "read": false, + "rule": 81, + "remote_identifier": "huo9z4" + } +}, +{ + "model": "core.post", + "pk": 3098, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.659Z", + "title": "Before and after being called a good boy", + "body": "
\"Before
", + "author": "vladgrinch", + "publication_date": "2020-07-20T10:48:40Z", + "url": "https://www.reddit.com/r/aww/comments/huhwu9/before_and_after_being_called_a_good_boy/", + "read": false, + "rule": 81, + "remote_identifier": "huhwu9" + } +}, +{ + "model": "core.post", + "pk": 3099, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.662Z", + "title": "My fianc\u00e9 has wanted a dog his whole life. This is his college graduation present. Welcome home Maple!", + "body": "
\"My
", + "author": "AlexisaurusRex", + "publication_date": "2020-07-20T17:57:25Z", + "url": "https://www.reddit.com/r/aww/comments/hunrie/my_fianc\u00e9_has_wanted_a_dog_his_whole_life_this_is/", + "read": false, + "rule": 81, + "remote_identifier": "hunrie" + } +}, +{ + "model": "core.post", + "pk": 3100, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.664Z", + "title": "Cute burro.", + "body": "
\"Cute
", + "author": "Craftmine101", + "publication_date": "2020-07-20T13:45:32Z", + "url": "https://www.reddit.com/r/aww/comments/huju40/cute_burro/", + "read": false, + "rule": 81, + "remote_identifier": "huju40" + } +}, +{ + "model": "core.post", + "pk": 3101, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.666Z", + "title": "I've never seen anyone dance better than that turtle.", + "body": "
", + "author": "Ashley1023", + "publication_date": "2020-07-20T18:07:30Z", + "url": "https://www.reddit.com/r/aww/comments/hunya8/ive_never_seen_anyone_dance_better_than_that/", + "read": false, + "rule": 81, + "remote_identifier": "hunya8" + } +}, +{ + "model": "core.post", + "pk": 3102, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.669Z", + "title": "Someone\u2019s going to be quite surprised when he realizes all this new stuff isn\u2019t for him!", + "body": "
\"Someone\u2019s
", + "author": "molly590", + "publication_date": "2020-07-20T15:46:21Z", + "url": "https://www.reddit.com/r/aww/comments/hulikg/someones_going_to_be_quite_surprised_when_he/", + "read": false, + "rule": 81, + "remote_identifier": "hulikg" + } +}, +{ + "model": "core.post", + "pk": 3103, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.671Z", + "title": "my aunt asked me to paint her puppy and I think it turned out so cute!!!", + "body": "
\"my
", + "author": "PineappleLightt", + "publication_date": "2020-07-20T16:39:37Z", + "url": "https://www.reddit.com/r/aww/comments/humea0/my_aunt_asked_me_to_paint_her_puppy_and_i_think/", + "read": false, + "rule": 81, + "remote_identifier": "humea0" + } +}, +{ + "model": "core.post", + "pk": 3104, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.673Z", + "title": "Master Assassin", + "body": "
\"Master
", + "author": "LauWalker", + "publication_date": "2020-07-20T18:47:52Z", + "url": "https://www.reddit.com/r/aww/comments/huop8a/master_assassin/", + "read": false, + "rule": 81, + "remote_identifier": "huop8a" + } +}, +{ + "model": "core.post", + "pk": 3105, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.675Z", + "title": "Every time this tank cleaner cleans out the aquarium, this fish swims over to him looking for pets", + "body": "", + "author": "unnaturalorder", + "publication_date": "2020-07-20T05:29:30Z", + "url": "https://www.reddit.com/r/aww/comments/hue3r0/every_time_this_tank_cleaner_cleans_out_the/", + "read": false, + "rule": 81, + "remote_identifier": "hue3r0" + } +}, +{ + "model": "core.post", + "pk": 3106, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.678Z", + "title": "My girlfriend sent me this while I was at work. And here I was thinking the perfect picture of our dog didn't exist", + "body": "", + "author": "Khuma-zi_Eldrama", + "publication_date": "2020-07-20T19:22:48Z", + "url": "https://www.reddit.com/r/aww/comments/hupdz8/my_girlfriend_sent_me_this_while_i_was_at_work/", + "read": false, + "rule": 81, + "remote_identifier": "hupdz8" + } +}, +{ + "model": "core.post", + "pk": 3107, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.680Z", + "title": "My first ever post, everyone meet my new baby girl Kiora! I\u2019m so in love with her\ud83e\udd7a\ud83d\udcab", + "body": "
\"My
", + "author": "Dumpling2463", + "publication_date": "2020-07-20T05:34:29Z", + "url": "https://www.reddit.com/r/aww/comments/hue6dx/my_first_ever_post_everyone_meet_my_new_baby_girl/", + "read": false, + "rule": 81, + "remote_identifier": "hue6dx" + } +}, +{ + "model": "core.post", + "pk": 3108, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.682Z", + "title": "Dog splashing in water", + "body": "", + "author": "TheRikari", + "publication_date": "2020-07-20T15:44:02Z", + "url": "https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/", + "read": false, + "rule": 81, + "remote_identifier": "hulh8k" + } +}, +{ + "model": "core.post", + "pk": 3109, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.685Z", + "title": "They say taking breaks is the key to productivity!", + "body": "
", + "author": "Thereaper29", + "publication_date": "2020-07-20T05:43:40Z", + "url": "https://www.reddit.com/r/aww/comments/hueawt/they_say_taking_breaks_is_the_key_to_productivity/", + "read": false, + "rule": 81, + "remote_identifier": "hueawt" + } +}, +{ + "model": "core.post", + "pk": 3110, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.687Z", + "title": "I went away for 3 weeks, and now my cat is in love with my husband", + "body": "
\"I
", + "author": "sillykittyish", + "publication_date": "2020-07-20T03:29:11Z", + "url": "https://www.reddit.com/r/aww/comments/hucd7u/i_went_away_for_3_weeks_and_now_my_cat_is_in_love/", + "read": false, + "rule": 81, + "remote_identifier": "hucd7u" + } +}, +{ + "model": "core.post", + "pk": 3111, + "fields": { + "created": "2020-07-20T19:32:35.636Z", + "modified": "2020-07-20T19:32:35.689Z", + "title": "Can you feel the love", + "body": "
", + "author": "kettySewrdPic", + "publication_date": "2020-07-20T09:13:32Z", + "url": "https://www.reddit.com/r/aww/comments/hugx1k/can_you_feel_the_love/", + "read": false, + "rule": 81, + "remote_identifier": "hugx1k" + } +}, +{ + "model": "core.post", + "pk": 3112, + "fields": { + "created": "2020-07-20T19:32:35.835Z", + "modified": "2020-07-21T20:14:50.522Z", + "title": "Linux Experiences/Rants or Education/Certifications thread - July 20, 2020", + "body": "

Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.

\n\n

Let us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.

\n\n

For those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!

\n\n

Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread.

\n
", + "author": "AutoModerator", + "publication_date": "2020-07-20T06:12:00Z", + "url": "https://www.reddit.com/r/linux/comments/hueoo0/linux_experiencesrants_or_educationcertifications/", + "read": false, + "rule": 80, + "remote_identifier": "hueoo0" + } +}, +{ + "model": "core.post", + "pk": 3113, + "fields": { + "created": "2020-07-20T19:32:35.836Z", + "modified": "2020-07-21T20:19:49.339Z", + "title": "Unix Family Tree", + "body": "
\"Unix
", + "author": "bauripalash", + "publication_date": "2020-07-20T10:32:15Z", + "url": "https://www.reddit.com/r/linux/comments/huhqrh/unix_family_tree/", + "read": true, + "rule": 80, + "remote_identifier": "huhqrh" + } +}, +{ + "model": "core.post", + "pk": 3114, + "fields": { + "created": "2020-07-20T19:32:35.836Z", + "modified": "2020-07-21T20:14:50.554Z", + "title": "NVIDIA open sourced part of NVAPI SDK to aid 'Windows emulation environments'", + "body": "", + "author": "ignapk", + "publication_date": "2020-07-20T13:17:19Z", + "url": "https://www.reddit.com/r/linux/comments/huji8c/nvidia_open_sourced_part_of_nvapi_sdk_to_aid/", + "read": false, + "rule": 80, + "remote_identifier": "huji8c" + } +}, +{ + "model": "core.post", + "pk": 3115, + "fields": { + "created": "2020-07-20T19:32:35.836Z", + "modified": "2020-07-21T20:14:50.551Z", + "title": "Jellyfin 10.6 released", + "body": "", + "author": "resoluti0n_", + "publication_date": "2020-07-20T16:40:05Z", + "url": "https://www.reddit.com/r/linux/comments/humekr/jellyfin_106_released/", + "read": false, + "rule": 80, + "remote_identifier": "humekr" + } +}, +{ + "model": "core.post", + "pk": 3116, + "fields": { + "created": "2020-07-20T19:32:35.836Z", + "modified": "2020-07-21T20:14:50.583Z", + "title": "[German] Article in major german newspaper about trying Linux and WSL. Literal: \"Why it's beneficial to try Linux now\"", + "body": "", + "author": "noname7890", + "publication_date": "2020-07-19T15:19:27Z", + "url": "https://www.reddit.com/r/linux/comments/hu0d5v/german_article_in_major_german_newspaper_about/", + "read": false, + "rule": 80, + "remote_identifier": "hu0d5v" + } +}, +{ + "model": "core.post", + "pk": 3117, + "fields": { + "created": "2020-07-20T19:32:35.837Z", + "modified": "2020-07-21T20:14:50.574Z", + "title": "Brian Kernighan: UNIX, C, AWK, AMPL, and Go Programming | AI Podcast #109 with Lex Fridman", + "body": "", + "author": "tinyatom", + "publication_date": "2020-07-20T08:48:35Z", + "url": "https://www.reddit.com/r/linux/comments/hugn0w/brian_kernighan_unix_c_awk_ampl_and_go/", + "read": false, + "rule": 80, + "remote_identifier": "hugn0w" + } +}, +{ + "model": "core.post", + "pk": 3118, + "fields": { + "created": "2020-07-20T19:32:35.837Z", + "modified": "2020-07-21T20:14:50.578Z", + "title": "Explaining Computers Host Christopher Barnatt Has Switched To Linux", + "body": "", + "author": "sysrpl", + "publication_date": "2020-07-20T13:00:02Z", + "url": "https://www.reddit.com/r/linux/comments/hujb12/explaining_computers_host_christopher_barnatt_has/", + "read": false, + "rule": 80, + "remote_identifier": "hujb12" + } +}, +{ + "model": "core.post", + "pk": 3119, + "fields": { + "created": "2020-07-20T19:32:35.837Z", + "modified": "2020-07-21T20:14:50.529Z", + "title": "Ireland donates contact tracing app to the Linux foundation.", + "body": "", + "author": "mathiasryan", + "publication_date": "2020-07-20T21:31:43Z", + "url": "https://www.reddit.com/r/linux/comments/hury4e/ireland_donates_contact_tracing_app_to_the_linux/", + "read": false, + "rule": 80, + "remote_identifier": "hury4e" + } +}, +{ + "model": "core.post", + "pk": 3120, + "fields": { + "created": "2020-07-20T19:32:35.842Z", + "modified": "2020-07-21T20:14:50.588Z", + "title": "I implemented a simple terminal-based password manager", + "body": "

I created a simple, secure, and free password manager written in C: SaltPass. I haven't contributed open source code before, but I think this might be useful to a few people. Especially as an alternative to paid solutions such as LastPass and the likes. Any suggestions/edits/code improvements would be greatly appreciated!

\n
", + "author": "zaid-gg", + "publication_date": "2020-07-20T07:43:03Z", + "url": "https://www.reddit.com/r/linux/comments/hufula/i_implemented_a_simple_terminalbased_password/", + "read": false, + "rule": 80, + "remote_identifier": "hufula" + } +}, +{ + "model": "core.post", + "pk": 3121, + "fields": { + "created": "2020-07-20T19:32:35.843Z", + "modified": "2020-07-21T20:14:50.593Z", + "title": "Performance analysis of multi services on container Docker, LXC, and LXD - Bulletin of Electrical Engineering and Informatics, Adinda Riztia Putri, Rendy Munadi, Ridha Muldina Negara Adaptive Network\u2026", + "body": "", + "author": "bmullan", + "publication_date": "2020-07-20T11:35:59Z", + "url": "https://www.reddit.com/r/linux/comments/huieio/performance_analysis_of_multi_services_on/", + "read": false, + "rule": 80, + "remote_identifier": "huieio" + } +}, +{ + "model": "core.post", + "pk": 3122, + "fields": { + "created": "2020-07-20T19:32:35.844Z", + "modified": "2020-07-21T20:14:50.602Z", + "title": "Create an Internal PKI using OpenSSL and NitroKey HSM", + "body": "", + "author": "PixelPaulaus", + "publication_date": "2020-07-20T06:18:41Z", + "url": "https://www.reddit.com/r/linux/comments/huerpn/create_an_internal_pki_using_openssl_and_nitrokey/", + "read": false, + "rule": 80, + "remote_identifier": "huerpn" + } +}, +{ + "model": "core.post", + "pk": 3123, + "fields": { + "created": "2020-07-20T19:32:35.844Z", + "modified": "2020-07-20T19:32:35.883Z", + "title": "vopono - run applications via VPNs with temporary network namespaces", + "body": "", + "author": "nivenkos", + "publication_date": "2020-07-19T20:02:57Z", + "url": "https://www.reddit.com/r/linux/comments/hu4vge/vopono_run_applications_via_vpns_with_temporary/", + "read": false, + "rule": 80, + "remote_identifier": "hu4vge" + } +}, +{ + "model": "core.post", + "pk": 3124, + "fields": { + "created": "2020-07-20T19:32:35.849Z", + "modified": "2020-07-20T19:32:35.886Z", + "title": "Double (triple, quadruple...) internet speed with openvpn tap channel bonding to a linux VPS", + "body": "

I have been working a couple of days on my latest video about channel bonding - the video is heavily inspired be this article on Serverfault. In essence, I have been searching for a while on how to bond multiple VPN channels together in order to increase internet speed - there does not seem to be a lot of information around - mainly articles on forums and reddit state that it should be possible but a detailed guide is hard to find. I am using two Ubuntu machines in order to build the connection - one local and one VPS. The bash scripts I use in my video in order to achieve tap channel bonding are available on my github repository. I am currently working on a second video in order to walk through and explain the scripts in depth. Enjoy!

\n\n

(EDIT) - the question has come up in the discussions below if this is really packet load balancing or rather balancing links only - please see my comment further down - I can confirm that this DOES packet balancing so it does work as described.

\n
", + "author": "onemarcfifty", + "publication_date": "2020-07-19T20:41:40Z", + "url": "https://www.reddit.com/r/linux/comments/hu5l4f/double_triple_quadruple_internet_speed_with/", + "read": false, + "rule": 80, + "remote_identifier": "hu5l4f" + } +}, +{ + "model": "core.post", + "pk": 3125, + "fields": { + "created": "2020-07-20T19:32:35.849Z", + "modified": "2020-07-20T19:32:35.888Z", + "title": "OpenRGB - Open source RGB lighting control that doesn't depend on manufacturer software, supports Linux", + "body": "", + "author": "pr0_c0d3", + "publication_date": "2020-07-18T16:52:48Z", + "url": "https://www.reddit.com/r/linux/comments/hthuli/openrgb_open_source_rgb_lighting_control_that/", + "read": false, + "rule": 80, + "remote_identifier": "hthuli" + } +}, +{ + "model": "core.post", + "pk": 3126, + "fields": { + "created": "2020-07-20T19:32:35.849Z", + "modified": "2020-07-20T19:32:35.890Z", + "title": "Make this any sense? Automatic CPU Speed & Power Optimizer", + "body": "", + "author": "spite77", + "publication_date": "2020-07-20T11:53:35Z", + "url": "https://www.reddit.com/r/linux/comments/huikxz/make_this_any_sense_automatic_cpu_speed_power/", + "read": false, + "rule": 80, + "remote_identifier": "huikxz" + } +}, +{ + "model": "core.post", + "pk": 3127, + "fields": { + "created": "2020-07-20T19:32:35.849Z", + "modified": "2020-07-20T19:32:35.891Z", + "title": "Let\u2019s not be pedantic about \u201cOpen Source\u201d", + "body": "", + "author": "speckz", + "publication_date": "2020-07-20T16:46:43Z", + "url": "https://www.reddit.com/r/linux/comments/humirw/lets_not_be_pedantic_about_open_source/", + "read": false, + "rule": 80, + "remote_identifier": "humirw" + } +}, +{ + "model": "core.post", + "pk": 3128, + "fields": { + "created": "2020-07-20T19:32:35.849Z", + "modified": "2020-07-20T19:32:35.893Z", + "title": "Experiences with running Linux Lite", + "body": "", + "author": "daemonpenguin", + "publication_date": "2020-07-20T02:43:49Z", + "url": "https://www.reddit.com/r/linux/comments/hubonw/experiences_with_running_linux_lite/", + "read": false, + "rule": 80, + "remote_identifier": "hubonw" + } +}, +{ + "model": "core.post", + "pk": 3129, + "fields": { + "created": "2020-07-20T19:32:35.849Z", + "modified": "2020-07-20T19:32:35.895Z", + "title": "Tried gnome on arch, surprised how lean it is (used flameshot so it used about 72mb more) closing at 600 megs) on fedora and pop i had gnome eating up 1.3gigs at boot up.", + "body": "
\"Tried
", + "author": "V1n0dKr1shna", + "publication_date": "2020-07-18T13:54:55Z", + "url": "https://www.reddit.com/r/linux/comments/htfeph/tried_gnome_on_arch_surprised_how_lean_it_is_used/", + "read": false, + "rule": 80, + "remote_identifier": "htfeph" + } +}, +{ + "model": "core.post", + "pk": 3130, + "fields": { + "created": "2020-07-20T19:32:35.849Z", + "modified": "2020-07-20T19:32:35.897Z", + "title": "The Free Software Foundation is holding a Fundraiser, help them reach 200 members", + "body": "", + "author": "Neet-Feet", + "publication_date": "2020-07-18T17:55:30Z", + "url": "https://www.reddit.com/r/linux/comments/htiuyi/the_free_software_foundation_is_holding_a/", + "read": false, + "rule": 80, + "remote_identifier": "htiuyi" + } +}, +{ + "model": "core.post", + "pk": 3131, + "fields": { + "created": "2020-07-20T19:32:35.853Z", + "modified": "2020-07-20T19:32:35.899Z", + "title": "Why is the mindset around Arch so negative?", + "body": "

I love the Linux community as a whole. You can find some of the most creative and imaginative people within most Linux communities. On a whole, Linux users are some of the most helpful and informative people you can encounter. Truly the type to think outside the box and learn new things. It can be very inspirational.

\n\n

If I jumped onto Ubuntu, Fedora, or openSUSE's community I can have a free flowing conversation about Linux, their distribution, and getting help or giving help is so free-flowing and easy. The communities are eager to welcome new people and appreciate folks who contribute.

\n\n

Then you have Arch. I love the OS but dislike the mindset. Asking for help is meat with resistance, giving help can also be punishable, and god forbid you try to have a discussion. But it's not just their core community either. For example, I just discovered Endeavour OS which is built around Arch and after 11 post I'm told to come back in 8 hours. Their subReddit here on Reddit, you have to ask to even make 1 post. There of course is also Manjaro Linux and they too have this gatekeeper mindset, the same can be said for ArcoLinux.

\n\n

What is it about Arch that makes everyone want to be either a control freak or a gatekeeper?

\n\n

I do not see this within the Ubuntu or Fedora or openSUSE communities. As I said, their mindset seems eager and willing to unite and work as a community. Am I the only how has noticed this?

\n
", + "author": "Linux-Is-Best", + "publication_date": "2020-07-18T23:28:12Z", + "url": "https://www.reddit.com/r/linux/comments/htojwk/why_is_the_mindset_around_arch_so_negative/", + "read": false, + "rule": 80, + "remote_identifier": "htojwk" + } +}, +{ + "model": "core.post", + "pk": 3132, + "fields": { + "created": "2020-07-20T19:32:35.853Z", + "modified": "2020-07-20T19:32:35.901Z", + "title": "Using the nstat network statistics command in Linux", + "body": "", + "author": "cronos426", + "publication_date": "2020-07-19T17:55:55Z", + "url": "https://www.reddit.com/r/linux/comments/hu2q6v/using_the_nstat_network_statistics_command_in/", + "read": false, + "rule": 80, + "remote_identifier": "hu2q6v" + } +}, +{ + "model": "core.post", + "pk": 3133, + "fields": { + "created": "2020-07-20T19:32:35.853Z", + "modified": "2020-07-20T19:32:35.903Z", + "title": "Contributing via GitLab Merge Requests", + "body": "", + "author": "ChristophCullmann", + "publication_date": "2020-07-18T20:01:26Z", + "url": "https://www.reddit.com/r/linux/comments/htl05p/contributing_via_gitlab_merge_requests/", + "read": false, + "rule": 80, + "remote_identifier": "htl05p" + } +}, +{ + "model": "core.post", + "pk": 3134, + "fields": { + "created": "2020-07-20T19:32:35.853Z", + "modified": "2020-07-20T19:32:35.905Z", + "title": "OpenMandriva: combines WINE64 and 32 into one package capable of running both binaries, i686 architecture was considered as deprecated. Work is underway on a new Rolling release", + "body": "", + "author": "DamonsLinux", + "publication_date": "2020-07-18T15:02:35Z", + "url": "https://www.reddit.com/r/linux/comments/htg9dj/openmandriva_combines_wine64_and_32_into_one/", + "read": false, + "rule": 80, + "remote_identifier": "htg9dj" + } +}, +{ + "model": "core.post", + "pk": 3135, + "fields": { + "created": "2020-07-20T19:32:35.853Z", + "modified": "2020-07-20T19:32:35.906Z", + "title": "OpenRCT2 Player Survey 2020 - Previous survey shows almost 25% players are linux, please help represent linux in the most recent survey", + "body": "", + "author": "christophski", + "publication_date": "2020-07-18T11:39:06Z", + "url": "https://www.reddit.com/r/linux/comments/htdzuh/openrct2_player_survey_2020_previous_survey_shows/", + "read": false, + "rule": 80, + "remote_identifier": "htdzuh" + } +}, +{ + "model": "core.post", + "pk": 3136, + "fields": { + "created": "2020-07-20T19:32:35.853Z", + "modified": "2020-07-20T19:32:35.908Z", + "title": "This week in KDE: Get New Stuff fixes and more", + "body": "", + "author": "kyentei", + "publication_date": "2020-07-18T10:03:46Z", + "url": "https://www.reddit.com/r/linux/comments/htd1an/this_week_in_kde_get_new_stuff_fixes_and_more/", + "read": false, + "rule": 80, + "remote_identifier": "htd1an" + } +}, +{ + "model": "core.post", + "pk": 3137, + "fields": { + "created": "2020-07-20T19:32:35.857Z", + "modified": "2020-07-20T19:32:35.910Z", + "title": "Blender Runs on Linux Pinephone", + "body": "

I managed to get the desktop version of Blender on the Pinephone, and it works really well except for a few bugs.

\n\n

See my post on r/blender:

\n\n

https://www.reddit.com/r/blender/comments/hsxv27/i_installed_blender_on_a_phone/

\n\n

and r/PINE64official:

\n\n

https://www.reddit.com/r/PINE64official/comments/hsxc33/blender_on_pine_phone_almost_usable/

\n\n

I've tried other desktop programs like Xournal and PPSSPP, their UIs also work well, I'd be able to do even more if OpenGL 3 was working.

\n
", + "author": "InfiniteHawk", + "publication_date": "2020-07-17T22:35:14Z", + "url": "https://www.reddit.com/r/linux/comments/ht3d4k/blender_runs_on_linux_pinephone/", + "read": false, + "rule": 80, + "remote_identifier": "ht3d4k" + } +}, +{ + "model": "core.post", + "pk": 3138, + "fields": { + "created": "2020-07-21T20:14:50.415Z", + "modified": "2020-07-21T20:18:21.616Z", + "title": "Hrmmm They Need to Fix Throttle Animations in the Sabre", + "body": "
", + "author": "TheBootRanger", + "publication_date": "2020-07-21T13:26:01Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv5omc/hrmmm_they_need_to_fix_throttle_animations_in_the/", + "read": true, + "rule": 82, + "remote_identifier": "hv5omc" + } +}, +{ + "model": "core.post", + "pk": 3139, + "fields": { + "created": "2020-07-21T20:14:50.415Z", + "modified": "2020-07-21T20:18:49.999Z", + "title": "My first 3.10 landing could have gone better...", + "body": "
", + "author": "KnLfey", + "publication_date": "2020-07-21T16:04:50Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv7w85/my_first_310_landing_could_have_gone_better/", + "read": true, + "rule": 82, + "remote_identifier": "hv7w85" + } +}, +{ + "model": "core.post", + "pk": 3140, + "fields": { + "created": "2020-07-21T20:14:50.415Z", + "modified": "2020-07-21T20:14:50.439Z", + "title": "How about the Christmas in 3 more years?", + "body": "
\"How
", + "author": "SpleanEater", + "publication_date": "2020-07-21T17:49:22Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv9qy8/how_about_the_christmas_in_3_more_years/", + "read": false, + "rule": 82, + "remote_identifier": "hv9qy8" + } +}, +{ + "model": "core.post", + "pk": 3141, + "fields": { + "created": "2020-07-21T20:14:50.415Z", + "modified": "2020-07-21T20:18:33.532Z", + "title": "Long time Elite Dangerous player. New to star citizen i think im doing great", + "body": "", + "author": "Filblo5", + "publication_date": "2020-07-21T15:33:49Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv7elb/long_time_elite_dangerous_player_new_to_star/", + "read": true, + "rule": 82, + "remote_identifier": "hv7elb" + } +}, +{ + "model": "core.post", + "pk": 3142, + "fields": { + "created": "2020-07-21T20:14:50.416Z", + "modified": "2020-07-21T20:14:50.443Z", + "title": "And we stand by it.", + "body": "
\"And
", + "author": "CyberTill", + "publication_date": "2020-07-21T18:57:48Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hvb3wm/and_we_stand_by_it/", + "read": false, + "rule": 82, + "remote_identifier": "hvb3wm" + } +}, +{ + "model": "core.post", + "pk": 3143, + "fields": { + "created": "2020-07-21T20:14:50.416Z", + "modified": "2020-07-21T20:14:50.446Z", + "title": "Nomad", + "body": "
\"Nomad\"
", + "author": "ibracitizen", + "publication_date": "2020-07-21T19:52:24Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hvc5h3/nomad/", + "read": false, + "rule": 82, + "remote_identifier": "hvc5h3" + } +}, +{ + "model": "core.post", + "pk": 3144, + "fields": { + "created": "2020-07-21T20:14:50.416Z", + "modified": "2020-07-21T20:14:50.449Z", + "title": "Probably the best screen cap i've ever caught on a whim. 3.5 Arc Corp release. Also a confession: I never pledged. Got a ship with my GPU. I intend to pay my dues.", + "body": "
\"Probably
", + "author": "ScionoicS", + "publication_date": "2020-07-21T20:23:01Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hvcqzf/probably_the_best_screen_cap_ive_ever_caught_on_a/", + "read": false, + "rule": 82, + "remote_identifier": "hvcqzf" + } +}, +{ + "model": "core.post", + "pk": 3145, + "fields": { + "created": "2020-07-21T20:14:50.416Z", + "modified": "2020-07-21T20:14:50.451Z", + "title": "Play to escape the depressing job hunt where I need 10 years experience for a entry level job to find this, only been playing for 1 and a half years :(", + "body": "
\"Play
", + "author": "Albert-III-", + "publication_date": "2020-07-21T12:23:45Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv4z08/play_to_escape_the_depressing_job_hunt_where_i/", + "read": false, + "rule": 82, + "remote_identifier": "hv4z08" + } +}, +{ + "model": "core.post", + "pk": 3146, + "fields": { + "created": "2020-07-21T20:14:50.416Z", + "modified": "2020-07-21T20:19:00.691Z", + "title": "The void beckons.", + "body": "
", + "author": "HisNameWasHis", + "publication_date": "2020-07-21T14:40:51Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv6nij/the_void_beckons/", + "read": true, + "rule": 82, + "remote_identifier": "hv6nij" + } +}, +{ + "model": "core.post", + "pk": 3147, + "fields": { + "created": "2020-07-21T20:14:50.416Z", + "modified": "2020-07-21T20:19:05.881Z", + "title": "I made a SC-like Photobash with Soldiers", + "body": "
\"I
", + "author": "IsaacPolar", + "publication_date": "2020-07-21T17:13:39Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv92ri/i_made_a_sclike_photobash_with_soldiers/", + "read": true, + "rule": 82, + "remote_identifier": "hv92ri" + } +}, +{ + "model": "core.post", + "pk": 3148, + "fields": { + "created": "2020-07-21T20:14:50.416Z", + "modified": "2020-07-21T20:19:41.227Z", + "title": "Ocean Shader Improvements", + "body": "
\"Ocean
", + "author": "shoeii", + "publication_date": "2020-07-21T18:41:51Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hvasds/ocean_shader_improvements/", + "read": true, + "rule": 82, + "remote_identifier": "hvasds" + } +}, +{ + "model": "core.post", + "pk": 3149, + "fields": { + "created": "2020-07-21T20:14:50.420Z", + "modified": "2020-07-21T20:14:50.459Z", + "title": "As much shit as Star Citizen (rightfully) gets it still does one thing better than any other 'game' I've played", + "body": "

It invokes a real sense of scale, on multiple levels.

\n\n

One could argue that's one of the most important feelings you'd want to capture in any game set in space, but of course it's mostly meaningless if there aren't enough gameplay loops and systems in place to work in tandem with and make the space that's been created interesting, and that's where SC is currently a failure.

\n\n

Even so, I think being able to create that sense of smallness isn't insignificant.

\n\n

You as a pilot are dwarfed by your ship which is itself dwarfed by a larger ship which is itself dwarfed by another, even more massive one which is dwarfed by the space station or hub you're at which is dwarfed by a crater on a moon which is dwarfed by the moon itself which is dwarfed by the planet it orbits which is dwarfed by the sheer vastness of space in between all of those things and that they are, despite the distance, still connected.

\n\n

Getting lost in Lorville (even if it is mostly linear) and knowing it's only a small part of the playable space is a really neat feeling - looking out from the windows of the train up into the sky and knowing you can go there and beyond really makes you feel like there is a whole world (and more) waiting to be explored.

\n\n

I think this is a direct result of having legs and not being locked into the cockpit of your ship - I've played more Elite: Dangerous than Star Citizen and it accomplishes a similar sense of scale but, at least not as far as I've felt, never to the same degree - because you're locked in your ship you never really get this same sense of being small or insignificant even though you are dwarfed in similar ways by planets/asteroids/other ships - will be interesting to see how their implementation of 'space legs' in the upcoming expansion changes this.

\n\n

My favourite thing to do in Star Citizen (because there isn't a whole lot) is to just find some pocket of space far away from anything else and just walk around my ship, feeling truly alone and insignificant, gazing out at the void that stretches infinitely all around - something about that is super comfy.

\n\n

I can't think of many other game that accomplish a similar level of scale though I'm sure they exist.

\n\n

I've been playing an indie game called Empyrion - Galactic Survival and it actually is sort of similar to SC in this regard but it's nowhere near as polished or smooth - transitions from atmosphere to space are not truly seamless and planets themselves are kind of stitched together, but it still manages to invoke that same kind of awe at the scale of things when you dock a small vessel to a capital vessel, for example - definitely worth checking out if you like sci-fi/space games, which you must if you're here, but just be prepared for the jank.

\n
", + "author": "thegreatself", + "publication_date": "2020-07-21T20:30:15Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hvcw38/as_much_shit_as_star_citizen_rightfully_gets_it/", + "read": false, + "rule": 82, + "remote_identifier": "hvcw38" + } +}, +{ + "model": "core.post", + "pk": 3150, + "fields": { + "created": "2020-07-21T20:14:50.420Z", + "modified": "2020-07-21T20:14:50.462Z", + "title": "You waiting for patch 3.10 to go live while watching tons of videos about the new flight model features. Be patient, 3.11 and 3.12 will be even better.", + "body": "
\"You
", + "author": "jsabater76", + "publication_date": "2020-07-21T09:39:27Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv372v/you_waiting_for_patch_310_to_go_live_while/", + "read": false, + "rule": 82, + "remote_identifier": "hv372v" + } +}, +{ + "model": "core.post", + "pk": 3151, + "fields": { + "created": "2020-07-21T20:14:50.420Z", + "modified": "2020-07-21T20:14:50.466Z", + "title": "CIG, can we please fix these \"black hole\" doors(when they are closed) on ships please.", + "body": "
\"CIG,
", + "author": "AbnormallyBendPenis", + "publication_date": "2020-07-21T13:40:14Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv5uzj/cig_can_we_please_fix_these_black_hole_doorswhen/", + "read": false, + "rule": 82, + "remote_identifier": "hv5uzj" + } +}, +{ + "model": "core.post", + "pk": 3152, + "fields": { + "created": "2020-07-21T20:14:50.420Z", + "modified": "2020-07-21T20:14:50.468Z", + "title": "Anvil Super Hornet over Cellin", + "body": "
\"Anvil
", + "author": "SaraCaterina", + "publication_date": "2020-07-21T20:33:58Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hvcyq6/anvil_super_hornet_over_cellin/", + "read": false, + "rule": 82, + "remote_identifier": "hvcyq6" + } +}, +{ + "model": "core.post", + "pk": 3153, + "fields": { + "created": "2020-07-21T20:14:50.420Z", + "modified": "2020-07-21T20:14:50.471Z", + "title": "3.10 Combat Changes", + "body": "", + "author": "STLYoungblood", + "publication_date": "2020-07-21T16:37:44Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv8fr7/310_combat_changes/", + "read": false, + "rule": 82, + "remote_identifier": "hv8fr7" + } +}, +{ + "model": "core.post", + "pk": 3154, + "fields": { + "created": "2020-07-21T20:14:50.420Z", + "modified": "2020-07-21T20:14:50.472Z", + "title": "Hey CIG how about that S42 Vi.... Oh...", + "body": "
\"Hey
", + "author": "SiEDeN", + "publication_date": "2020-07-21T21:37:16Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hve6am/hey_cig_how_about_that_s42_vi_oh/", + "read": false, + "rule": 82, + "remote_identifier": "hve6am" + } +}, +{ + "model": "core.post", + "pk": 3155, + "fields": { + "created": "2020-07-21T20:14:50.422Z", + "modified": "2020-07-21T20:14:50.475Z", + "title": "3.10 M PTU Eclipse improvements", + "body": "

If this goes live, CIG had addressed 2 of my Eclipse critics.

\n\n

Not because of my videos of course, CIG doesn't know I exist.

\n\n

 

\n\n

a. Eclipse has armor stealth in 3.10, see my table:\nhttps://docs.google.com/spreadsheets/d/1OJXg7MQsG_IVTPsmlmZYaxEPK4n4iqnhQx4oigIlJHg/edit#gid=343807746

\n\n

 

\n\n

b. Eclipse can fire her size 9 torpedoes way quicker now, see my video with a side by side comparison of the max firing speed in 3.9 and 3.10:\nhttps://youtu.be/GFTF1Qt7T3o?t=207

\n
", + "author": "Camural", + "publication_date": "2020-07-21T18:15:50Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hva9lc/310_m_ptu_eclipse_improvements/", + "read": false, + "rule": 82, + "remote_identifier": "hva9lc" + } +}, +{ + "model": "core.post", + "pk": 3156, + "fields": { + "created": "2020-07-21T20:14:50.422Z", + "modified": "2020-07-21T20:14:50.477Z", + "title": "Hark! The Drake Herald Sings", + "body": "
\"Hark!
", + "author": "CyrexStorm", + "publication_date": "2020-07-21T16:19:31Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv84kk/hark_the_drake_herald_sings/", + "read": false, + "rule": 82, + "remote_identifier": "hv84kk" + } +}, +{ + "model": "core.post", + "pk": 3157, + "fields": { + "created": "2020-07-21T20:14:50.422Z", + "modified": "2020-07-21T20:14:50.479Z", + "title": "The new flight stick in the Prowler", + "body": "
\"The
", + "author": "Potato_Nades", + "publication_date": "2020-07-21T16:22:22Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv86c2/the_new_flight_stick_in_the_prowler/", + "read": false, + "rule": 82, + "remote_identifier": "hv86c2" + } +}, +{ + "model": "core.post", + "pk": 3158, + "fields": { + "created": "2020-07-21T20:14:50.422Z", + "modified": "2020-07-21T20:14:50.481Z", + "title": "Norwegian VAT charged from August 1st", + "body": "
\"Norwegian
", + "author": "norgeek", + "publication_date": "2020-07-21T10:30:57Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv3r3l/norwegian_vat_charged_from_august_1st/", + "read": false, + "rule": 82, + "remote_identifier": "hv3r3l" + } +}, +{ + "model": "core.post", + "pk": 3159, + "fields": { + "created": "2020-07-21T20:14:50.423Z", + "modified": "2020-07-21T20:14:50.484Z", + "title": "With Pyro (currently WIP), Nyx (partially done), Odin (S42), currently on the way, what is everyone\u2019s thoughts on Terra possibly being next on the list of star systems to be added into the PU within \u2026", + "body": "
\"With
", + "author": "realCLTotaku", + "publication_date": "2020-07-21T13:27:09Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hv5p41/with_pyro_currently_wip_nyx_partially_done_odin/", + "read": false, + "rule": 82, + "remote_identifier": "hv5p41" + } +}, +{ + "model": "core.post", + "pk": 3160, + "fields": { + "created": "2020-07-21T20:14:50.423Z", + "modified": "2020-07-21T20:14:50.486Z", + "title": "Testing out the new electron rifle", + "body": "
", + "author": "joshbaker2112", + "publication_date": "2020-07-21T02:56:19Z", + "url": "https://www.reddit.com/r/starcitizen/comments/huxr6d/testing_out_the_new_electron_rifle/", + "read": false, + "rule": 82, + "remote_identifier": "huxr6d" + } +}, +{ + "model": "core.post", + "pk": 3161, + "fields": { + "created": "2020-07-21T20:14:50.423Z", + "modified": "2020-07-21T20:14:50.487Z", + "title": "Imperial Geographic's Lovecraftian magazine special is here. \ud83d\udc19 Find the link in the comments!", + "body": "
\"Imperial
", + "author": "Good_Punk2", + "publication_date": "2020-07-21T18:21:38Z", + "url": "https://www.reddit.com/r/starcitizen/comments/hvadrh/imperial_geographics_lovecraftian_magazine/", + "read": false, + "rule": 82, + "remote_identifier": "hvadrh" + } +}, +{ + "model": "core.post", + "pk": 3162, + "fields": { + "created": "2020-07-21T20:14:50.497Z", + "modified": "2020-07-21T20:14:50.525Z", + "title": "Linux Distributions Timeline", + "body": "
\"Linux
", + "author": "bauripalash", + "publication_date": "2020-07-21T06:07:59Z", + "url": "https://www.reddit.com/r/linux/comments/hv0ktn/linux_distributions_timeline/", + "read": false, + "rule": 80, + "remote_identifier": "hv0ktn" + } +}, +{ + "model": "core.post", + "pk": 3163, + "fields": { + "created": "2020-07-21T20:14:50.497Z", + "modified": "2020-07-21T20:14:50.527Z", + "title": "Fedora: Proposal to replace default wined3d backend with DXVK", + "body": "", + "author": "friskfrugt", + "publication_date": "2020-07-21T19:42:49Z", + "url": "https://www.reddit.com/r/linux/comments/hvbyyr/fedora_proposal_to_replace_default_wined3d/", + "read": false, + "rule": 80, + "remote_identifier": "hvbyyr" + } +}, +{ + "model": "core.post", + "pk": 3164, + "fields": { + "created": "2020-07-21T20:14:50.497Z", + "modified": "2020-07-21T20:14:50.531Z", + "title": "Update on marketing and communication plans for the LibreOffice 7.x series", + "body": "", + "author": "TheQuantumZero", + "publication_date": "2020-07-21T09:59:23Z", + "url": "https://www.reddit.com/r/linux/comments/hv3erm/update_on_marketing_and_communication_plans_for/", + "read": false, + "rule": 80, + "remote_identifier": "hv3erm" + } +}, +{ + "model": "core.post", + "pk": 3165, + "fields": { + "created": "2020-07-21T20:14:50.497Z", + "modified": "2020-07-21T20:14:50.533Z", + "title": "FOSS job opening: LibreOffice Development Mentor at The Document Foundation", + "body": "", + "author": "themikeosguy", + "publication_date": "2020-07-21T14:26:36Z", + "url": "https://www.reddit.com/r/linux/comments/hv6gfw/foss_job_opening_libreoffice_development_mentor/", + "read": false, + "rule": 80, + "remote_identifier": "hv6gfw" + } +}, +{ + "model": "core.post", + "pk": 3166, + "fields": { + "created": "2020-07-21T20:14:50.503Z", + "modified": "2020-07-21T20:14:50.536Z", + "title": "gomd - quickly display formatted markdown files with code highlight in your browser", + "body": "

Hi all!

\n\n

I wanted to share a project I've been working on recently. I think it reached a stage where it's pretty usable and should work out of the box. gomd sets up a HTTP server and serves a directory in your browser so you can quickly view your markdown files. It comes with some neat features like:

\n\n
    \n
  • Monitoring files - it will monitor files for changes and reload them whenever needed
  • \n
  • Hot reloading - whenever the file you are currently viewing changes, the tab in your browser will reload automatically.
  • \n
  • Code Highlight - All blocks of code in most common languages will be color highlighted.
  • \n
  • Themes - choose from multiple themes like: solarized, monokai, github, dracula...
  • \n
\n\n

Link: gomd

\n\n

For now its only available from AUR or built from source.

\n\n

\n\n

Any tips or feedback will be greatly appreciated :)

\n
", + "author": "wwojtekk", + "publication_date": "2020-07-21T20:07:31Z", + "url": "https://www.reddit.com/r/linux/comments/hvcg44/gomd_quickly_display_formatted_markdown_files/", + "read": false, + "rule": 80, + "remote_identifier": "hvcg44" + } +}, +{ + "model": "core.post", + "pk": 3167, + "fields": { + "created": "2020-07-21T20:14:50.503Z", + "modified": "2020-07-21T20:14:50.543Z", + "title": "They're not otherwise wrong, but it didn't become a real Internet standard until 2017.", + "body": "
\"They're
", + "author": "foodown", + "publication_date": "2020-07-21T21:39:09Z", + "url": "https://www.reddit.com/r/linux/comments/hve7l5/theyre_not_otherwise_wrong_but_it_didnt_become_a/", + "read": false, + "rule": 80, + "remote_identifier": "hve7l5" + } +}, +{ + "model": "core.post", + "pk": 3168, + "fields": { + "created": "2020-07-21T20:14:50.503Z", + "modified": "2020-07-21T20:14:50.545Z", + "title": "Drawing - an alternative to Paint for Linux (gtk3, support HiDPI)", + "body": "", + "author": "dontdieych", + "publication_date": "2020-07-21T02:37:22Z", + "url": "https://www.reddit.com/r/linux/comments/huxgsg/drawing_an_alternative_to_paint_for_linux_gtk3/", + "read": false, + "rule": 80, + "remote_identifier": "huxgsg" + } +}, +{ + "model": "core.post", + "pk": 3169, + "fields": { + "created": "2020-07-21T20:14:50.509Z", + "modified": "2020-07-21T20:14:50.547Z", + "title": "Observations on a Linux issue with 3.5mm earphones with a mic", + "body": "

Alright hello. I have come from r/SolusProject and I made a post there to do with headphone issues. I suggest you read through the post and comments to get a better understanding before reading this https://www.reddit.com/r/SolusProject/comments/hsql4d/frustrating_headphone_issues/. I had posted to do with it again, but it got taken down for duplication (when it wasn't duplication). This post is more of my observations from experimenting and such. There are distros I haven't tried but I tried a wide range of distros like manjaro, ubuntu based ones and all solus flavors, and I was looking more for how well they worked out of the box, rather than with fiddling around with pulse, hdajack etc which I know will work eventually. If you stumbled across this from searching about the same issue I have (or similar) or are confused to what this is about, I suggest you look at my previous post also.

\n\n

So anyways, I've tried the past few days mounting isos to usb drives and trying live os and installing various distros to see about the headphone issue. And my conclusion is that this issue affects the linux kernel in some way across the board. I don't really understand why completely but I have some kind of idea.

\n\n

From installing fresh distros, I noticed that the earphones (they are 3.5mm earphones + mic) get recognised as a microphone and not as a speaker system of some kind. Every single time I had a look at the sound settings and in pulse, they came up as plugged microphone, with the internal speakers being the only output device every single time. It's really odd seeing as how ubuntu 14.04 and xubuntu etc from years past worked flawlessly with the earphones, even manjaro a while ago on my older craptop worked fine. I don't really understand why it doesn't work on my device now.

\n\n

I'll leave my specs at the bottom of this post but what I think is is there's something the manufacturer did, or something like the cpu causes issue with linux. The manufacturer of my laptop is Lenovo, and the cpu/igpu is from AMD. A warning sign is that when installing a linux distro, it doesn't bring up the dual boot menu at startup like it should. Instead it completely hides the fact it exists until I use something like easyuefi to add an option for that distro, how it works is you specify the boot partition, whether it's linux or windows and the loader conf file for the distro. All of this hassle everytime doesn't appear on my craptop, because the dual boot menu appears flawlessly without issue. May be because it uses an Intel cpu/igpu unlike my newer laptop but it's hard to say.

\n\n

Also, it seems like the devices that appear in a given distro when looking at alsa, is hd generic devices but by reloading alsa or any command that shows the full name of the device, it says it's Intel. I don't know if that would be an issue, maybe amd use intel sound drivers or something. It's odd nonetheless.

\n\n

This issue has been boggling my mind for obvious reasons, with half-rhetorical questions like does linux not support the earphones anymore, whether out of accident from an overlooked bug in an update or intentionally phasing out? Is any of this AMD or Lenovo's fault? Even with proper headphones or something, will they fail? I don't think anyone here really knows, hell I'd bet an extreme that no one really understands why in the linux community. I kinda rambled in this post with stuff that should've been said in the last post/thread, but I'm saying it now.

\n\n

Thanks for contributing thus far to this discussion in figuring this out.

\n\n

Specs: AMD Ryzen 5 3500U Mobile CPU (2.2 - 3.7ghz quad core)

\n\n

Radeon Vega 8 Integrated GPU, 8GB Ram, 256GB SSD.

\n\n

Lenovo C340-14API Laptop

\n
", + "author": "BrianMeerkatlol", + "publication_date": "2020-07-21T21:02:19Z", + "url": "https://www.reddit.com/r/linux/comments/hvdi3o/observations_on_a_linux_issue_with_35mm_earphones/", + "read": false, + "rule": 80, + "remote_identifier": "hvdi3o" + } +}, +{ + "model": "core.post", + "pk": 3170, + "fields": { + "created": "2020-07-21T20:14:50.509Z", + "modified": "2020-07-21T20:14:50.549Z", + "title": "South Korean distro HamoniKR OS has been added to Distrowatch", + "body": "", + "author": "TheHordeRisesAgain", + "publication_date": "2020-07-21T07:44:21Z", + "url": "https://www.reddit.com/r/linux/comments/hv1ug1/south_korean_distro_hamonikr_os_has_been_added_to/", + "read": false, + "rule": 80, + "remote_identifier": "hv1ug1" + } +}, +{ + "model": "core.post", + "pk": 3171, + "fields": { + "created": "2020-07-21T20:14:50.509Z", + "modified": "2020-07-21T20:14:50.559Z", + "title": "The Jailer is free! New release of the outstanding database subsetter and browser is available.", + "body": "", + "author": "Plane-Discussion", + "publication_date": "2020-07-21T12:53:54Z", + "url": "https://www.reddit.com/r/linux/comments/hv5b0j/the_jailer_is_free_new_release_of_the_outstanding/", + "read": false, + "rule": 80, + "remote_identifier": "hv5b0j" + } +}, +{ + "model": "core.post", + "pk": 3172, + "fields": { + "created": "2020-07-21T20:14:50.513Z", + "modified": "2020-07-21T20:14:50.563Z", + "title": "A few very well-aged excerpts from Microsoft\u2019s infamous 2004 \u201cGet the facts\u201d campaign, where they make the case for Windows servers being cheaper, more secure, and more performant than Linux servers", + "body": "
\n

Get the facts on Windows and Linux.

\n\n

Leading companies and third-party analysts confirm it: Windows has a lower total cost of ownership and outperforms Linux.

\n\n

...

\n\n

-Security

\n\n

Windows Users Have Fewer Vulnerabilities

\n
\n\n

And then literally the very next bullet point:

\n\n
\n

-Featured Customer Case Study

\n\n

Equifax

\n\n

Equifax Sees 14 Percent Cost Savings

\n\n

Find out why Equifax, a global leader in transforming data into intelligence, selected Windows over Linux to enhance the speed and performance of its marketing services capabilities. Using Microsoft Windows Server System, the company has seen 14 percent in cost savings over Linux.

\n
\n\n

Good thing they saved 14% and got all that extra security! Sure their website is janky and their login flow is downright horrifying (Check it out if you want to be amazed), but who could blame them? Linux is \u201cProhibitively Expensive, Extremely Complex, and Provides No Tangible Business Gains\u201d, Microsoft said so!

\n\n

Source: https://web.archive.org/web/20041027003759/http://www.microsoft.com/windowsserversystem/facts/default.mspx

\n
", + "author": "kevinhaze", + "publication_date": "2020-07-20T21:42:15Z", + "url": "https://www.reddit.com/r/linux/comments/hus5lz/a_few_very_wellaged_excerpts_from_microsofts/", + "read": false, + "rule": 80, + "remote_identifier": "hus5lz" + } +}, +{ + "model": "core.post", + "pk": 3173, + "fields": { + "created": "2020-07-21T20:14:50.515Z", + "modified": "2020-07-21T20:14:50.566Z", + "title": "Are there are any professional audio recording studios or artists that use Linux?", + "body": "

As the title says, who is using Linux as a professional audio engineer, producer, or artist? I am a former Mac user myself, and I am seeing people from time to time who have become disillusioned with what Apple has been doing for the past few years. However, I'm not sure if Linux really has a place for these people to land if they are serious about what they do.

\n\n

Fedora Design Suite and Ubuntu Studio are definitely encouraging to see, but what is their real-world usage like? Are we getting better with professional audio in Linux, or have things been stagnant for years?

\n
", + "author": "RootHouston", + "publication_date": "2020-07-21T00:08:26Z", + "url": "https://www.reddit.com/r/linux/comments/huuxvq/are_there_are_any_professional_audio_recording/", + "read": false, + "rule": 80, + "remote_identifier": "huuxvq" + } +}, +{ + "model": "core.post", + "pk": 3174, + "fields": { + "created": "2020-07-21T20:14:50.515Z", + "modified": "2020-07-21T20:14:50.570Z", + "title": "When Linux had marketing", + "body": "", + "author": "Commodore256", + "publication_date": "2020-07-21T14:03:56Z", + "url": "https://www.reddit.com/r/linux/comments/hv65oa/when_linux_had_marketing/", + "read": false, + "rule": 80, + "remote_identifier": "hv65oa" + } +}, +{ + "model": "core.post", + "pk": 3175, + "fields": { + "created": "2020-07-21T20:14:50.520Z", + "modified": "2020-07-21T20:14:50.598Z", + "title": "Ward: Simple and minimalistic server dashboard", + "body": "

Ward is a simple and and minimalistic server monitoring tool. Ward supports adaptive design system. Also it supports dark theme. It shows only principal information and can be used, if you want to see nice looking dashboard instead looking on bunch of numbers and graphs. Ward works nice on all popular operating systems, because it uses OSHI.

\n\n

https://preview.redd.it/gdppswc3a3c51.png?width=1448&format=png&auto=webp&s=0d6e10146c105ddcfd045dd59c970d4c127ddb8c

\n\n

https://github.com/B-Software/Ward

\n
", + "author": "Pabyzu", + "publication_date": "2020-07-21T00:33:40Z", + "url": "https://www.reddit.com/r/linux/comments/huvea3/ward_simple_and_minimalistic_server_dashboard/", + "read": false, + "rule": 80, + "remote_identifier": "huvea3" + } +}, +{ + "model": "core.post", + "pk": 3176, + "fields": { + "created": "2020-07-21T20:14:50.522Z", + "modified": "2020-07-21T20:14:50.606Z", + "title": "WindowsFX - a good Windows alternative?", + "body": "

I would personally like to hear some of your opinions (in the replies) about WindowsFX. What is WindowsFX you may ask? WindowsFX is a Brazilian linux distribution that is designed to look and act like Windows 10.

\n\n

Linux / WindowsFX is based off of Ubuntu, and uses Cinnamon as its DE. Upon first boot, normal Windows users can tell the difference. But if you were to put it in front of a non tech-savvy person, they wouldn't be able to tell the difference.

\n\n

Personally, with WSL on Windows, I see no need for a distro like this. However, as I said, I would like to hear your opinions on this distro.

\n\n

Video review here.

\n
", + "author": "Demonitized101", + "publication_date": "2020-07-20T23:03:29Z", + "url": "https://www.reddit.com/r/linux/comments/hutpt5/windowsfx_a_good_windows_alternative/", + "read": false, + "rule": 80, + "remote_identifier": "hutpt5" + } +}, +{ + "model": "core.post", + "pk": 3177, + "fields": { + "created": "2020-07-21T20:14:50.775Z", + "modified": "2020-07-21T20:14:50.780Z", + "title": "Every day this good boy brings a carrot to his best buddy", + "body": "
", + "author": "TooShiftyForYou", + "publication_date": "2020-07-21T15:25:31Z", + "url": "https://www.reddit.com/r/aww/comments/hv7a8b/every_day_this_good_boy_brings_a_carrot_to_his/", + "read": false, + "rule": 81, + "remote_identifier": "hv7a8b" + } +}, +{ + "model": "core.post", + "pk": 3178, + "fields": { + "created": "2020-07-21T20:14:50.775Z", + "modified": "2020-07-25T20:08:34.264Z", + "title": "Kitten mimics his human petting the dog", + "body": "
", + "author": "SpecterAscendant", + "publication_date": "2020-07-21T14:56:57Z", + "url": "https://www.reddit.com/r/aww/comments/hv6ve3/kitten_mimics_his_human_petting_the_dog/", + "read": true, + "rule": 81, + "remote_identifier": "hv6ve3" + } +}, +{ + "model": "core.post", + "pk": 3179, + "fields": { + "created": "2020-07-21T20:14:50.775Z", + "modified": "2020-07-21T20:14:50.789Z", + "title": "My fox friend!", + "body": "
", + "author": "Zepantha", + "publication_date": "2020-07-21T14:27:25Z", + "url": "https://www.reddit.com/r/aww/comments/hv6gte/my_fox_friend/", + "read": false, + "rule": 81, + "remote_identifier": "hv6gte" + } +}, +{ + "model": "core.post", + "pk": 3180, + "fields": { + "created": "2020-07-21T20:14:50.775Z", + "modified": "2020-07-21T20:15:46.876Z", + "title": "Ducks annihilate peas", + "body": "
", + "author": "tommycalibre", + "publication_date": "2020-07-21T17:12:40Z", + "url": "https://www.reddit.com/r/aww/comments/hv9258/ducks_annihilate_peas/", + "read": true, + "rule": 81, + "remote_identifier": "hv9258" + } +}, +{ + "model": "core.post", + "pk": 3181, + "fields": { + "created": "2020-07-21T20:14:50.775Z", + "modified": "2020-07-21T20:14:50.797Z", + "title": "Wiggle it baby", + "body": "
", + "author": "neo_star", + "publication_date": "2020-07-21T18:44:31Z", + "url": "https://www.reddit.com/r/aww/comments/hvaucy/wiggle_it_baby/", + "read": false, + "rule": 81, + "remote_identifier": "hvaucy" + } +}, +{ + "model": "core.post", + "pk": 3182, + "fields": { + "created": "2020-07-21T20:14:50.776Z", + "modified": "2020-07-21T20:16:22.725Z", + "title": "I guess I should do this.. everyone seems to be liking little pups and kittens so.. Reddit, meet bailey", + "body": "
\"I
", + "author": "X_XNOTHINGX_X", + "publication_date": "2020-07-21T14:15:08Z", + "url": "https://www.reddit.com/r/aww/comments/hv6b0a/i_guess_i_should_do_this_everyone_seems_to_be/", + "read": true, + "rule": 81, + "remote_identifier": "hv6b0a" + } +}, +{ + "model": "core.post", + "pk": 3183, + "fields": { + "created": "2020-07-21T20:14:50.776Z", + "modified": "2020-07-21T20:14:50.806Z", + "title": "The hat makes the crab.", + "body": "
\"The
", + "author": "fujfuj", + "publication_date": "2020-07-21T14:48:40Z", + "url": "https://www.reddit.com/r/aww/comments/hv6rde/the_hat_makes_the_crab/", + "read": false, + "rule": 81, + "remote_identifier": "hv6rde" + } +}, +{ + "model": "core.post", + "pk": 3184, + "fields": { + "created": "2020-07-21T20:14:50.776Z", + "modified": "2020-07-21T20:14:50.812Z", + "title": "Baby bunny fits in hand", + "body": "
", + "author": "Hawken10", + "publication_date": "2020-07-21T12:31:30Z", + "url": "https://www.reddit.com/r/aww/comments/hv5253/baby_bunny_fits_in_hand/", + "read": false, + "rule": 81, + "remote_identifier": "hv5253" + } +}, +{ + "model": "core.post", + "pk": 3185, + "fields": { + "created": "2020-07-21T20:14:50.776Z", + "modified": "2020-07-21T20:14:50.818Z", + "title": "My cat and I, both pregnant", + "body": "
\"My
", + "author": "nixdionisio", + "publication_date": "2020-07-21T11:06:25Z", + "url": "https://www.reddit.com/r/aww/comments/hv44m2/my_cat_and_i_both_pregnant/", + "read": false, + "rule": 81, + "remote_identifier": "hv44m2" + } +}, +{ + "model": "core.post", + "pk": 3186, + "fields": { + "created": "2020-07-21T20:14:50.776Z", + "modified": "2020-07-21T20:14:50.822Z", + "title": "Very sweet dance", + "body": "
", + "author": "Ashley1023", + "publication_date": "2020-07-21T13:03:03Z", + "url": "https://www.reddit.com/r/aww/comments/hv5ewq/very_sweet_dance/", + "read": false, + "rule": 81, + "remote_identifier": "hv5ewq" + } +}, +{ + "model": "core.post", + "pk": 3187, + "fields": { + "created": "2020-07-21T20:14:50.776Z", + "modified": "2020-07-21T20:14:50.825Z", + "title": "My local pet-store has a cat named Vegemite \u2764\ufe0f", + "body": "
\"My
", + "author": "galinhad", + "publication_date": "2020-07-21T12:06:17Z", + "url": "https://www.reddit.com/r/aww/comments/hv4s5z/my_local_petstore_has_a_cat_named_vegemite/", + "read": false, + "rule": 81, + "remote_identifier": "hv4s5z" + } +}, +{ + "model": "core.post", + "pk": 3188, + "fields": { + "created": "2020-07-21T20:14:50.777Z", + "modified": "2020-07-21T20:15:01.459Z", + "title": "A teacher like that makes a huge difference", + "body": "
", + "author": "Unicornglitteryblood", + "publication_date": "2020-07-21T18:29:57Z", + "url": "https://www.reddit.com/r/aww/comments/hvajo9/a_teacher_like_that_makes_a_huge_difference/", + "read": true, + "rule": 81, + "remote_identifier": "hvajo9" + } +}, +{ + "model": "core.post", + "pk": 3189, + "fields": { + "created": "2020-07-21T20:14:50.777Z", + "modified": "2020-07-22T19:55:49.930Z", + "title": "Kitten Encounters Bubbly Water", + "body": "
\"Kitten
", + "author": "DragonOBunny", + "publication_date": "2020-07-21T15:28:05Z", + "url": "https://www.reddit.com/r/aww/comments/hv7bis/kitten_encounters_bubbly_water/", + "read": true, + "rule": 81, + "remote_identifier": "hv7bis" + } +}, +{ + "model": "core.post", + "pk": 3190, + "fields": { + "created": "2020-07-21T20:14:50.777Z", + "modified": "2020-07-21T20:14:50.833Z", + "title": "Are These My Chickens Now?", + "body": "", + "author": "jasontaken", + "publication_date": "2020-07-21T09:55:36Z", + "url": "https://www.reddit.com/r/aww/comments/hv3de1/are_these_my_chickens_now/", + "read": false, + "rule": 81, + "remote_identifier": "hv3de1" + } +}, +{ + "model": "core.post", + "pk": 3191, + "fields": { + "created": "2020-07-21T20:14:50.777Z", + "modified": "2020-07-25T20:08:20.518Z", + "title": "Our St Bernard 6 months apart", + "body": "
\"Our
", + "author": "ryan3105", + "publication_date": "2020-07-21T18:00:04Z", + "url": "https://www.reddit.com/r/aww/comments/hv9yea/our_st_bernard_6_months_apart/", + "read": true, + "rule": 81, + "remote_identifier": "hv9yea" + } +}, +{ + "model": "core.post", + "pk": 3192, + "fields": { + "created": "2020-07-21T20:14:50.777Z", + "modified": "2020-07-21T20:14:50.837Z", + "title": "Father and child in sync", + "body": "
", + "author": "Araragi_Monogatari", + "publication_date": "2020-07-21T08:29:18Z", + "url": "https://www.reddit.com/r/aww/comments/hv2enj/father_and_child_in_sync/", + "read": false, + "rule": 81, + "remote_identifier": "hv2enj" + } +}, +{ + "model": "core.post", + "pk": 3193, + "fields": { + "created": "2020-07-21T20:14:50.778Z", + "modified": "2020-07-21T20:14:50.840Z", + "title": "A meme is born", + "body": "
\"A
", + "author": "Unicornglitteryblood", + "publication_date": "2020-07-21T18:55:04Z", + "url": "https://www.reddit.com/r/aww/comments/hvb1vh/a_meme_is_born/", + "read": false, + "rule": 81, + "remote_identifier": "hvb1vh" + } +}, +{ + "model": "core.post", + "pk": 3194, + "fields": { + "created": "2020-07-21T20:14:50.778Z", + "modified": "2020-07-21T20:14:50.842Z", + "title": "She bites, then she sleeps, then bites again, then sleeps again. \ud83d\ude02", + "body": "
", + "author": "earlymauvs", + "publication_date": "2020-07-21T11:34:19Z", + "url": "https://www.reddit.com/r/aww/comments/hv4fat/she_bites_then_she_sleeps_then_bites_again_then/", + "read": false, + "rule": 81, + "remote_identifier": "hv4fat" + } +}, +{ + "model": "core.post", + "pk": 3195, + "fields": { + "created": "2020-07-21T20:14:50.778Z", + "modified": "2020-07-21T20:14:50.844Z", + "title": "Nothing calmer that 2 ginger cats rubbing heads and showing their love in morning", + "body": "
\"Nothing
", + "author": "Apotheosis33", + "publication_date": "2020-07-21T08:39:24Z", + "url": "https://www.reddit.com/r/aww/comments/hv2j2g/nothing_calmer_that_2_ginger_cats_rubbing_heads/", + "read": false, + "rule": 81, + "remote_identifier": "hv2j2g" + } +}, +{ + "model": "core.post", + "pk": 3196, + "fields": { + "created": "2020-07-21T20:14:50.778Z", + "modified": "2020-07-21T20:14:50.851Z", + "title": "Ring Tailed Possum", + "body": "", + "author": "Wayward-Delver", + "publication_date": "2020-07-21T11:23:51Z", + "url": "https://www.reddit.com/r/aww/comments/hv4b9e/ring_tailed_possum/", + "read": false, + "rule": 81, + "remote_identifier": "hv4b9e" + } +}, +{ + "model": "core.post", + "pk": 3197, + "fields": { + "created": "2020-07-21T20:14:50.778Z", + "modified": "2020-07-21T20:14:50.854Z", + "title": "Baby scooby in sad mood....", + "body": "
\"Baby
", + "author": "deepanshuahiroo7", + "publication_date": "2020-07-21T15:12:23Z", + "url": "https://www.reddit.com/r/aww/comments/hv73ft/baby_scooby_in_sad_mood/", + "read": false, + "rule": 81, + "remote_identifier": "hv73ft" + } +}, +{ + "model": "core.post", + "pk": 3198, + "fields": { + "created": "2020-07-21T20:14:50.779Z", + "modified": "2020-07-21T20:14:50.856Z", + "title": "New friends!", + "body": "
\"New
", + "author": "HelentotheKeller", + "publication_date": "2020-07-21T13:10:48Z", + "url": "https://www.reddit.com/r/aww/comments/hv5i6i/new_friends/", + "read": false, + "rule": 81, + "remote_identifier": "hv5i6i" + } +}, +{ + "model": "core.post", + "pk": 3199, + "fields": { + "created": "2020-07-21T20:14:50.779Z", + "modified": "2020-07-21T20:14:50.858Z", + "title": "When you haven't chewed anything for 1 second", + "body": "
\"When
", + "author": "Tanay4", + "publication_date": "2020-07-21T10:26:53Z", + "url": "https://www.reddit.com/r/aww/comments/hv3pl0/when_you_havent_chewed_anything_for_1_second/", + "read": false, + "rule": 81, + "remote_identifier": "hv3pl0" + } +}, +{ + "model": "core.post", + "pk": 3200, + "fields": { + "created": "2020-07-21T20:14:50.779Z", + "modified": "2020-07-21T20:17:01.490Z", + "title": "Mango Derp", + "body": "
\"Mango
", + "author": "sheetglass", + "publication_date": "2020-07-21T13:27:26Z", + "url": "https://www.reddit.com/r/aww/comments/hv5p8s/mango_derp/", + "read": true, + "rule": 81, + "remote_identifier": "hv5p8s" + } +}, +{ + "model": "core.post", + "pk": 3201, + "fields": { + "created": "2020-07-21T20:14:50.779Z", + "modified": "2020-07-21T20:14:50.863Z", + "title": "My guy turns 20 next month", + "body": "
\"My
", + "author": "alozsoc", + "publication_date": "2020-07-21T06:34:26Z", + "url": "https://www.reddit.com/r/aww/comments/hv0xp1/my_guy_turns_20_next_month/", + "read": false, + "rule": 81, + "remote_identifier": "hv0xp1" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add log entry", + "content_type": [ + "admin", + "logentry" + ], + "codename": "add_logentry" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change log entry", + "content_type": [ + "admin", + "logentry" + ], + "codename": "change_logentry" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete log entry", + "content_type": [ + "admin", + "logentry" + ], + "codename": "delete_logentry" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view log entry", + "content_type": [ + "admin", + "logentry" + ], + "codename": "view_logentry" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add permission", + "content_type": [ + "auth", + "permission" + ], + "codename": "add_permission" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change permission", + "content_type": [ + "auth", + "permission" + ], + "codename": "change_permission" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete permission", + "content_type": [ + "auth", + "permission" + ], + "codename": "delete_permission" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view permission", + "content_type": [ + "auth", + "permission" + ], + "codename": "view_permission" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add group", + "content_type": [ + "auth", + "group" + ], + "codename": "add_group" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change group", + "content_type": [ + "auth", + "group" + ], + "codename": "change_group" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete group", + "content_type": [ + "auth", + "group" + ], + "codename": "delete_group" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view group", + "content_type": [ + "auth", + "group" + ], + "codename": "view_group" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add content type", + "content_type": [ + "contenttypes", + "contenttype" + ], + "codename": "add_contenttype" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change content type", + "content_type": [ + "contenttypes", + "contenttype" + ], + "codename": "change_contenttype" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete content type", + "content_type": [ + "contenttypes", + "contenttype" + ], + "codename": "delete_contenttype" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view content type", + "content_type": [ + "contenttypes", + "contenttype" + ], + "codename": "view_contenttype" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add session", + "content_type": [ + "sessions", + "session" + ], + "codename": "add_session" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change session", + "content_type": [ + "sessions", + "session" + ], + "codename": "change_session" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete session", + "content_type": [ + "sessions", + "session" + ], + "codename": "delete_session" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view session", + "content_type": [ + "sessions", + "session" + ], + "codename": "view_session" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add crontab", + "content_type": [ + "django_celery_beat", + "crontabschedule" + ], + "codename": "add_crontabschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change crontab", + "content_type": [ + "django_celery_beat", + "crontabschedule" + ], + "codename": "change_crontabschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete crontab", + "content_type": [ + "django_celery_beat", + "crontabschedule" + ], + "codename": "delete_crontabschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view crontab", + "content_type": [ + "django_celery_beat", + "crontabschedule" + ], + "codename": "view_crontabschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add interval", + "content_type": [ + "django_celery_beat", + "intervalschedule" + ], + "codename": "add_intervalschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change interval", + "content_type": [ + "django_celery_beat", + "intervalschedule" + ], + "codename": "change_intervalschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete interval", + "content_type": [ + "django_celery_beat", + "intervalschedule" + ], + "codename": "delete_intervalschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view interval", + "content_type": [ + "django_celery_beat", + "intervalschedule" + ], + "codename": "view_intervalschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add periodic task", + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "codename": "add_periodictask" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change periodic task", + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "codename": "change_periodictask" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete periodic task", + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "codename": "delete_periodictask" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view periodic task", + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "codename": "view_periodictask" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add periodic tasks", + "content_type": [ + "django_celery_beat", + "periodictasks" + ], + "codename": "add_periodictasks" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change periodic tasks", + "content_type": [ + "django_celery_beat", + "periodictasks" + ], + "codename": "change_periodictasks" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete periodic tasks", + "content_type": [ + "django_celery_beat", + "periodictasks" + ], + "codename": "delete_periodictasks" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view periodic tasks", + "content_type": [ + "django_celery_beat", + "periodictasks" + ], + "codename": "view_periodictasks" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add solar event", + "content_type": [ + "django_celery_beat", + "solarschedule" + ], + "codename": "add_solarschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change solar event", + "content_type": [ + "django_celery_beat", + "solarschedule" + ], + "codename": "change_solarschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete solar event", + "content_type": [ + "django_celery_beat", + "solarschedule" + ], + "codename": "delete_solarschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view solar event", + "content_type": [ + "django_celery_beat", + "solarschedule" + ], + "codename": "view_solarschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add clocked", + "content_type": [ + "django_celery_beat", + "clockedschedule" + ], + "codename": "add_clockedschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change clocked", + "content_type": [ + "django_celery_beat", + "clockedschedule" + ], + "codename": "change_clockedschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete clocked", + "content_type": [ + "django_celery_beat", + "clockedschedule" + ], + "codename": "delete_clockedschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view clocked", + "content_type": [ + "django_celery_beat", + "clockedschedule" + ], + "codename": "view_clockedschedule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add registration profile", + "content_type": [ + "registration", + "registrationprofile" + ], + "codename": "add_registrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change registration profile", + "content_type": [ + "registration", + "registrationprofile" + ], + "codename": "change_registrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete registration profile", + "content_type": [ + "registration", + "registrationprofile" + ], + "codename": "delete_registrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view registration profile", + "content_type": [ + "registration", + "registrationprofile" + ], + "codename": "view_registrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add supervised registration profile", + "content_type": [ + "registration", + "supervisedregistrationprofile" + ], + "codename": "add_supervisedregistrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change supervised registration profile", + "content_type": [ + "registration", + "supervisedregistrationprofile" + ], + "codename": "change_supervisedregistrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete supervised registration profile", + "content_type": [ + "registration", + "supervisedregistrationprofile" + ], + "codename": "delete_supervisedregistrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view supervised registration profile", + "content_type": [ + "registration", + "supervisedregistrationprofile" + ], + "codename": "view_supervisedregistrationprofile" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add access attempt", + "content_type": [ + "axes", + "accessattempt" + ], + "codename": "add_accessattempt" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change access attempt", + "content_type": [ + "axes", + "accessattempt" + ], + "codename": "change_accessattempt" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete access attempt", + "content_type": [ + "axes", + "accessattempt" + ], + "codename": "delete_accessattempt" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view access attempt", + "content_type": [ + "axes", + "accessattempt" + ], + "codename": "view_accessattempt" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add access log", + "content_type": [ + "axes", + "accesslog" + ], + "codename": "add_accesslog" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change access log", + "content_type": [ + "axes", + "accesslog" + ], + "codename": "change_accesslog" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete access log", + "content_type": [ + "axes", + "accesslog" + ], + "codename": "delete_accesslog" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view access log", + "content_type": [ + "axes", + "accesslog" + ], + "codename": "view_accesslog" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add user", + "content_type": [ + "accounts", + "user" + ], + "codename": "add_user" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change user", + "content_type": [ + "accounts", + "user" + ], + "codename": "change_user" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete user", + "content_type": [ + "accounts", + "user" + ], + "codename": "delete_user" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view user", + "content_type": [ + "accounts", + "user" + ], + "codename": "view_user" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add post", + "content_type": [ + "core", + "post" + ], + "codename": "add_post" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change post", + "content_type": [ + "core", + "post" + ], + "codename": "change_post" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete post", + "content_type": [ + "core", + "post" + ], + "codename": "delete_post" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view post", + "content_type": [ + "core", + "post" + ], + "codename": "view_post" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add Category", + "content_type": [ + "core", + "category" + ], + "codename": "add_category" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change Category", + "content_type": [ + "core", + "category" + ], + "codename": "change_category" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete Category", + "content_type": [ + "core", + "category" + ], + "codename": "delete_category" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view Category", + "content_type": [ + "core", + "category" + ], + "codename": "view_category" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can add collection rule", + "content_type": [ + "collection", + "collectionrule" + ], + "codename": "add_collectionrule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can change collection rule", + "content_type": [ + "collection", + "collectionrule" + ], + "codename": "change_collectionrule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can delete collection rule", + "content_type": [ + "collection", + "collectionrule" + ], + "codename": "delete_collectionrule" + } +}, +{ + "model": "auth.permission", + "fields": { + "name": "Can view collection rule", + "content_type": [ + "collection", + "collectionrule" + ], + "codename": "view_collectionrule" + } +}, +{ + "model": "accounts.user", + "fields": { + "password": "pbkdf2_sha256$180000$U9a2CS9X0b8Y$T6bD/VoUOFoGNIp16aFlOL0N7q0e6A3I97ypm/AhsGo=", + "last_login": "2020-07-21T20:14:35.966Z", + "is_superuser": true, + "first_name": "", + "last_name": "", + "is_staff": true, + "is_active": true, + "date_joined": "2019-07-18T18:52:36.080Z", + "email": "sonny@bakker.nl", + "reddit_refresh_token": null, + "reddit_access_token": null, + "groups": [], + "user_permissions": [] + } +}, +{ + "model": "core.category", + "pk": 8, + "fields": { + "created": "2019-11-17T19:37:24.671Z", + "modified": "2019-11-18T19:59:55.010Z", + "name": "World news", + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "core.category", + "pk": 9, + "fields": { + "created": "2019-11-17T19:37:26.161Z", + "modified": "2020-05-30T13:36:10.509Z", + "name": "Tech", + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 3, + "fields": { + "created": "2019-07-14T13:08:10.374Z", + "modified": "2020-07-14T11:45:30.680Z", + "name": "Hackers News", + "type": "feed", + "url": "https://news.ycombinator.com/rss", + "website_url": "https://news.ycombinator.com/", + "favicon": "https://news.ycombinator.com/favicon.ico", + "timezone": "UTC", + "category": 9, + "last_run": "2020-07-14T11:45:30.477Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 4, + "fields": { + "created": "2019-07-20T11:24:32.745Z", + "modified": "2020-07-14T11:45:29.357Z", + "name": "BBC", + "type": "feed", + "url": "http://feeds.bbci.co.uk/news/world/rss.xml", + "website_url": "https://www.bbc.co.uk/news/", + "favicon": "https://m.files.bbci.co.uk/modules/bbc-morph-news-waf-page-meta/2.5.2/apple-touch-icon-57x57-precomposed.png", + "timezone": "UTC", + "category": 8, + "last_run": "2020-07-14T11:45:28.863Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 5, + "fields": { + "created": "2019-07-20T11:24:50.411Z", + "modified": "2020-07-14T11:45:30.063Z", + "name": "Ars Technica", + "type": "feed", + "url": "http://feeds.arstechnica.com/arstechnica/index?fmt=xml", + "website_url": "https://arstechnica.com", + "favicon": "https://cdn.arstechnica.net/favicon.ico", + "timezone": "UTC", + "category": 9, + "last_run": "2020-07-14T11:45:29.810Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 6, + "fields": { + "created": "2019-07-20T11:25:02.089Z", + "modified": "2020-07-14T11:45:30.473Z", + "name": "The Guardian", + "type": "feed", + "url": "https://www.theguardian.com/world/rss", + "website_url": "https://www.theguardian.com/world", + "favicon": "https://assets.guim.co.uk/images/favicons/873381bf11d58e20f551905d51575117/72x72.png", + "timezone": "UTC", + "category": 8, + "last_run": "2020-07-14T11:45:30.181Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 7, + "fields": { + "created": "2019-07-20T11:25:30.121Z", + "modified": "2020-07-14T11:45:29.807Z", + "name": "Tweakers", + "type": "feed", + "url": "http://feeds.feedburner.com/tweakers/mixed?fmt=xml", + "website_url": "https://tweakers.net/", + "favicon": null, + "timezone": "UTC", + "category": 9, + "last_run": "2020-07-14T11:45:29.525Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 8, + "fields": { + "created": "2019-07-20T11:25:46.256Z", + "modified": "2020-07-14T11:45:30.179Z", + "name": "The Verge", + "type": "feed", + "url": "https://www.theverge.com/rss/index.xml", + "website_url": "https://www.theverge.com/", + "favicon": "https://cdn.vox-cdn.com/uploads/chorus_asset/file/7395367/favicon-16x16.0.png", + "timezone": "UTC", + "category": 9, + "last_run": "2020-07-14T11:45:30.066Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 9, + "fields": { + "created": "2019-11-24T15:28:41.399Z", + "modified": "2020-07-14T11:45:29.522Z", + "name": "NOS", + "type": "feed", + "url": "http://feeds.nos.nl/nosnieuwsalgemeen", + "website_url": null, + "favicon": null, + "timezone": "Europe/Amsterdam", + "category": 8, + "last_run": "2020-07-14T11:45:29.362Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 80, + "fields": { + "created": "2020-07-08T19:30:10.638Z", + "modified": "2020-07-21T20:14:50.609Z", + "name": "Linux subreddit", + "type": "subreddit", + "url": "https://oauth.reddit.com/r/linux/hot", + "website_url": null, + "favicon": null, + "timezone": "UTC", + "category": 9, + "last_run": "2020-07-21T20:14:50.492Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 81, + "fields": { + "created": "2020-07-08T19:30:33.590Z", + "modified": "2020-07-21T20:14:50.865Z", + "name": "AWW subreddit", + "type": "subreddit", + "url": "https://oauth.reddit.com/r/aww/hot", + "website_url": null, + "favicon": null, + "timezone": "UTC", + "category": 8, + "last_run": "2020-07-21T20:14:50.768Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "collection.collectionrule", + "pk": 82, + "fields": { + "created": "2020-07-20T19:29:37.675Z", + "modified": "2020-07-21T20:14:50.489Z", + "name": "Star citizen subreddit", + "type": "subreddit", + "url": "https://oauth.reddit.com/r/starcitizen/hot.json", + "website_url": null, + "favicon": null, + "timezone": "UTC", + "category": 9, + "last_run": "2020-07-21T20:14:50.355Z", + "succeeded": true, + "error": null, + "enabled": true, + "user": [ + "sonny@bakker.nl" + ] + } +}, +{ + "model": "admin.logentry", + "pk": 1, + "fields": { + "action_time": "2020-05-24T18:38:44.624Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "django_celery_beat", + "intervalschedule" + ], + "object_id": "5", + "object_repr": "every 4 hours", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 2, + "fields": { + "action_time": "2020-05-24T18:38:46.689Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "object_id": "10", + "object_repr": "sonny@bakker.nl-collection-task: every 4 hours", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Interval Schedule\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 3, + "fields": { + "action_time": "2020-05-24T18:39:09.203Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "object_id": "26", + "object_repr": "sonnyba871@gmail.com-collection-task: every hour", + "action_flag": 3, + "change_message": "" + } +}, +{ + "model": "admin.logentry", + "pk": 4, + "fields": { + "action_time": "2020-05-24T19:46:50.248Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "object_id": "10", + "object_repr": "sonny@bakker.nl-collection-task: every 4 hours", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Positional Arguments\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 5, + "fields": { + "action_time": "2020-07-07T19:37:57.086Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "accounts", + "user" + ], + "object_id": "1", + "object_repr": "sonny@bakker.nl", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Reddit refresh token\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 6, + "fields": { + "action_time": "2020-07-07T19:39:46.160Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "object_id": "10", + "object_repr": "sonny@bakker.nl-collection-task: every 4 hours", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Task (registered)\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 7, + "fields": { + "action_time": "2020-07-08T19:29:27.025Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "django_celery_beat", + "periodictask" + ], + "object_id": "11", + "object_repr": "Reddit collection task: every 4 hours", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 8, + "fields": { + "action_time": "2020-07-14T11:46:50.039Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "accounts", + "user" + ], + "object_id": "1", + "object_repr": "sonny@bakker.nl", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Reddit access token\", \"Reddit refresh token\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 9, + "fields": { + "action_time": "2020-07-18T19:08:33.997Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "collection", + "collectionrule" + ], + "object_id": "81", + "object_repr": "AWW subreddit", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Url\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 10, + "fields": { + "action_time": "2020-07-18T19:08:44.063Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "collection", + "collectionrule" + ], + "object_id": "80", + "object_repr": "Linux subreddit", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Url\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 11, + "fields": { + "action_time": "2020-07-18T19:17:25.213Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "core", + "post" + ], + "object_id": "2336", + "object_repr": "Post-2336", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Body\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 12, + "fields": { + "action_time": "2020-07-18T19:17:40.596Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "core", + "post" + ], + "object_id": "2336", + "object_repr": "Post-2336", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Body\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 13, + "fields": { + "action_time": "2020-07-19T10:55:55.807Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "core", + "post" + ], + "object_id": "2764", + "object_repr": "Post-2764", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Body\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 14, + "fields": { + "action_time": "2020-07-19T10:57:40.643Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "core", + "post" + ], + "object_id": "2764", + "object_repr": "Post-2764", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Body\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 15, + "fields": { + "action_time": "2020-07-19T10:58:05.823Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "core", + "post" + ], + "object_id": "2764", + "object_repr": "Post-2764", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Body\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 16, + "fields": { + "action_time": "2020-07-26T09:51:52.478Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "accounts", + "user" + ], + "object_id": "1", + "object_repr": "sonny@bakker.nl", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"First name\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 17, + "fields": { + "action_time": "2020-07-26T09:52:04.691Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "accounts", + "user" + ], + "object_id": "1", + "object_repr": "sonny@bakker.nl", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"password\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 18, + "fields": { + "action_time": "2020-07-26T09:52:12.392Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "accounts", + "user" + ], + "object_id": "1", + "object_repr": "sonny@bakker.nl", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"First name\"]}}]" + } +}, +{ + "model": "admin.logentry", + "pk": 19, + "fields": { + "action_time": "2020-07-26T09:56:15.949Z", + "user": [ + "sonny@bakker.nl" + ], + "content_type": [ + "accounts", + "user" + ], + "object_id": "1", + "object_repr": "sonny@bakker.nl", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Reddit access token\", \"Reddit refresh token\"]}}]" + } +} +] diff --git a/src/newsreader/fixtures/fixture.json b/src/newsreader/fixtures/fixture.json deleted file mode 100644 index 05e61e7..0000000 --- a/src/newsreader/fixtures/fixture.json +++ /dev/null @@ -1,1066 +0,0 @@ -[ - { - "model": "auth.permission", - "pk": 1, - "fields": { - "name": "Can add log entry", - "content_type": 1, - "codename": "add_logentry" - } - }, - { - "model": "auth.permission", - "pk": 2, - "fields": { - "name": "Can change log entry", - "content_type": 1, - "codename": "change_logentry" - } - }, - { - "model": "auth.permission", - "pk": 3, - "fields": { - "name": "Can delete log entry", - "content_type": 1, - "codename": "delete_logentry" - } - }, - { - "model": "auth.permission", - "pk": 4, - "fields": { - "name": "Can view log entry", - "content_type": 1, - "codename": "view_logentry" - } - }, - { - "model": "auth.permission", - "pk": 5, - "fields": { - "name": "Can add permission", - "content_type": 2, - "codename": "add_permission" - } - }, - { - "model": "auth.permission", - "pk": 6, - "fields": { - "name": "Can change permission", - "content_type": 2, - "codename": "change_permission" - } - }, - { - "model": "auth.permission", - "pk": 7, - "fields": { - "name": "Can delete permission", - "content_type": 2, - "codename": "delete_permission" - } - }, - { - "model": "auth.permission", - "pk": 8, - "fields": { - "name": "Can view permission", - "content_type": 2, - "codename": "view_permission" - } - }, - { - "model": "auth.permission", - "pk": 9, - "fields": { - "name": "Can add group", - "content_type": 3, - "codename": "add_group" - } - }, - { - "model": "auth.permission", - "pk": 10, - "fields": { - "name": "Can change group", - "content_type": 3, - "codename": "change_group" - } - }, - { - "model": "auth.permission", - "pk": 11, - "fields": { - "name": "Can delete group", - "content_type": 3, - "codename": "delete_group" - } - }, - { - "model": "auth.permission", - "pk": 12, - "fields": { - "name": "Can view group", - "content_type": 3, - "codename": "view_group" - } - }, - { - "model": "auth.permission", - "pk": 13, - "fields": { - "name": "Can add content type", - "content_type": 4, - "codename": "add_contenttype" - } - }, - { - "model": "auth.permission", - "pk": 14, - "fields": { - "name": "Can change content type", - "content_type": 4, - "codename": "change_contenttype" - } - }, - { - "model": "auth.permission", - "pk": 15, - "fields": { - "name": "Can delete content type", - "content_type": 4, - "codename": "delete_contenttype" - } - }, - { - "model": "auth.permission", - "pk": 16, - "fields": { - "name": "Can view content type", - "content_type": 4, - "codename": "view_contenttype" - } - }, - { - "model": "auth.permission", - "pk": 17, - "fields": { - "name": "Can add session", - "content_type": 5, - "codename": "add_session" - } - }, - { - "model": "auth.permission", - "pk": 18, - "fields": { - "name": "Can change session", - "content_type": 5, - "codename": "change_session" - } - }, - { - "model": "auth.permission", - "pk": 19, - "fields": { - "name": "Can delete session", - "content_type": 5, - "codename": "delete_session" - } - }, - { - "model": "auth.permission", - "pk": 20, - "fields": { - "name": "Can view session", - "content_type": 5, - "codename": "view_session" - } - }, - { - "model": "auth.permission", - "pk": 21, - "fields": { - "name": "Can add crontab", - "content_type": 6, - "codename": "add_crontabschedule" - } - }, - { - "model": "auth.permission", - "pk": 22, - "fields": { - "name": "Can change crontab", - "content_type": 6, - "codename": "change_crontabschedule" - } - }, - { - "model": "auth.permission", - "pk": 23, - "fields": { - "name": "Can delete crontab", - "content_type": 6, - "codename": "delete_crontabschedule" - } - }, - { - "model": "auth.permission", - "pk": 24, - "fields": { - "name": "Can view crontab", - "content_type": 6, - "codename": "view_crontabschedule" - } - }, - { - "model": "auth.permission", - "pk": 25, - "fields": { - "name": "Can add interval", - "content_type": 7, - "codename": "add_intervalschedule" - } - }, - { - "model": "auth.permission", - "pk": 26, - "fields": { - "name": "Can change interval", - "content_type": 7, - "codename": "change_intervalschedule" - } - }, - { - "model": "auth.permission", - "pk": 27, - "fields": { - "name": "Can delete interval", - "content_type": 7, - "codename": "delete_intervalschedule" - } - }, - { - "model": "auth.permission", - "pk": 28, - "fields": { - "name": "Can view interval", - "content_type": 7, - "codename": "view_intervalschedule" - } - }, - { - "model": "auth.permission", - "pk": 29, - "fields": { - "name": "Can add periodic task", - "content_type": 8, - "codename": "add_periodictask" - } - }, - { - "model": "auth.permission", - "pk": 30, - "fields": { - "name": "Can change periodic task", - "content_type": 8, - "codename": "change_periodictask" - } - }, - { - "model": "auth.permission", - "pk": 31, - "fields": { - "name": "Can delete periodic task", - "content_type": 8, - "codename": "delete_periodictask" - } - }, - { - "model": "auth.permission", - "pk": 32, - "fields": { - "name": "Can view periodic task", - "content_type": 8, - "codename": "view_periodictask" - } - }, - { - "model": "auth.permission", - "pk": 33, - "fields": { - "name": "Can add periodic tasks", - "content_type": 9, - "codename": "add_periodictasks" - } - }, - { - "model": "auth.permission", - "pk": 34, - "fields": { - "name": "Can change periodic tasks", - "content_type": 9, - "codename": "change_periodictasks" - } - }, - { - "model": "auth.permission", - "pk": 35, - "fields": { - "name": "Can delete periodic tasks", - "content_type": 9, - "codename": "delete_periodictasks" - } - }, - { - "model": "auth.permission", - "pk": 36, - "fields": { - "name": "Can view periodic tasks", - "content_type": 9, - "codename": "view_periodictasks" - } - }, - { - "model": "auth.permission", - "pk": 37, - "fields": { - "name": "Can add solar event", - "content_type": 10, - "codename": "add_solarschedule" - } - }, - { - "model": "auth.permission", - "pk": 38, - "fields": { - "name": "Can change solar event", - "content_type": 10, - "codename": "change_solarschedule" - } - }, - { - "model": "auth.permission", - "pk": 39, - "fields": { - "name": "Can delete solar event", - "content_type": 10, - "codename": "delete_solarschedule" - } - }, - { - "model": "auth.permission", - "pk": 40, - "fields": { - "name": "Can view solar event", - "content_type": 10, - "codename": "view_solarschedule" - } - }, - { - "model": "auth.permission", - "pk": 41, - "fields": { - "name": "Can add clocked", - "content_type": 11, - "codename": "add_clockedschedule" - } - }, - { - "model": "auth.permission", - "pk": 42, - "fields": { - "name": "Can change clocked", - "content_type": 11, - "codename": "change_clockedschedule" - } - }, - { - "model": "auth.permission", - "pk": 43, - "fields": { - "name": "Can delete clocked", - "content_type": 11, - "codename": "delete_clockedschedule" - } - }, - { - "model": "auth.permission", - "pk": 44, - "fields": { - "name": "Can view clocked", - "content_type": 11, - "codename": "view_clockedschedule" - } - }, - { - "model": "auth.permission", - "pk": 45, - "fields": { - "name": "Can add registration profile", - "content_type": 12, - "codename": "add_registrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 46, - "fields": { - "name": "Can change registration profile", - "content_type": 12, - "codename": "change_registrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 47, - "fields": { - "name": "Can delete registration profile", - "content_type": 12, - "codename": "delete_registrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 48, - "fields": { - "name": "Can view registration profile", - "content_type": 12, - "codename": "view_registrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 49, - "fields": { - "name": "Can add supervised registration profile", - "content_type": 13, - "codename": "add_supervisedregistrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 50, - "fields": { - "name": "Can change supervised registration profile", - "content_type": 13, - "codename": "change_supervisedregistrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 51, - "fields": { - "name": "Can delete supervised registration profile", - "content_type": 13, - "codename": "delete_supervisedregistrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 52, - "fields": { - "name": "Can view supervised registration profile", - "content_type": 13, - "codename": "view_supervisedregistrationprofile" - } - }, - { - "model": "auth.permission", - "pk": 53, - "fields": { - "name": "Can add access attempt", - "content_type": 14, - "codename": "add_accessattempt" - } - }, - { - "model": "auth.permission", - "pk": 54, - "fields": { - "name": "Can change access attempt", - "content_type": 14, - "codename": "change_accessattempt" - } - }, - { - "model": "auth.permission", - "pk": 55, - "fields": { - "name": "Can delete access attempt", - "content_type": 14, - "codename": "delete_accessattempt" - } - }, - { - "model": "auth.permission", - "pk": 56, - "fields": { - "name": "Can view access attempt", - "content_type": 14, - "codename": "view_accessattempt" - } - }, - { - "model": "auth.permission", - "pk": 57, - "fields": { - "name": "Can add access log", - "content_type": 15, - "codename": "add_accesslog" - } - }, - { - "model": "auth.permission", - "pk": 58, - "fields": { - "name": "Can change access log", - "content_type": 15, - "codename": "change_accesslog" - } - }, - { - "model": "auth.permission", - "pk": 59, - "fields": { - "name": "Can delete access log", - "content_type": 15, - "codename": "delete_accesslog" - } - }, - { - "model": "auth.permission", - "pk": 60, - "fields": { - "name": "Can view access log", - "content_type": 15, - "codename": "view_accesslog" - } - }, - { - "model": "auth.permission", - "pk": 61, - "fields": { - "name": "Can add access failure", - "content_type": 16, - "codename": "add_accessfailurelog" - } - }, - { - "model": "auth.permission", - "pk": 62, - "fields": { - "name": "Can change access failure", - "content_type": 16, - "codename": "change_accessfailurelog" - } - }, - { - "model": "auth.permission", - "pk": 63, - "fields": { - "name": "Can delete access failure", - "content_type": 16, - "codename": "delete_accessfailurelog" - } - }, - { - "model": "auth.permission", - "pk": 64, - "fields": { - "name": "Can view access failure", - "content_type": 16, - "codename": "view_accessfailurelog" - } - }, - { - "model": "auth.permission", - "pk": 65, - "fields": { - "name": "Can add user", - "content_type": 17, - "codename": "add_user" - } - }, - { - "model": "auth.permission", - "pk": 66, - "fields": { - "name": "Can change user", - "content_type": 17, - "codename": "change_user" - } - }, - { - "model": "auth.permission", - "pk": 67, - "fields": { - "name": "Can delete user", - "content_type": 17, - "codename": "delete_user" - } - }, - { - "model": "auth.permission", - "pk": 68, - "fields": { - "name": "Can view user", - "content_type": 17, - "codename": "view_user" - } - }, - { - "model": "auth.permission", - "pk": 69, - "fields": { - "name": "Can add post", - "content_type": 18, - "codename": "add_post" - } - }, - { - "model": "auth.permission", - "pk": 70, - "fields": { - "name": "Can change post", - "content_type": 18, - "codename": "change_post" - } - }, - { - "model": "auth.permission", - "pk": 71, - "fields": { - "name": "Can delete post", - "content_type": 18, - "codename": "delete_post" - } - }, - { - "model": "auth.permission", - "pk": 72, - "fields": { - "name": "Can view post", - "content_type": 18, - "codename": "view_post" - } - }, - { - "model": "auth.permission", - "pk": 73, - "fields": { - "name": "Can add Category", - "content_type": 19, - "codename": "add_category" - } - }, - { - "model": "auth.permission", - "pk": 74, - "fields": { - "name": "Can change Category", - "content_type": 19, - "codename": "change_category" - } - }, - { - "model": "auth.permission", - "pk": 75, - "fields": { - "name": "Can delete Category", - "content_type": 19, - "codename": "delete_category" - } - }, - { - "model": "auth.permission", - "pk": 76, - "fields": { - "name": "Can view Category", - "content_type": 19, - "codename": "view_category" - } - }, - { - "model": "auth.permission", - "pk": 77, - "fields": { - "name": "Can add collection rule", - "content_type": 20, - "codename": "add_collectionrule" - } - }, - { - "model": "auth.permission", - "pk": 78, - "fields": { - "name": "Can change collection rule", - "content_type": 20, - "codename": "change_collectionrule" - } - }, - { - "model": "auth.permission", - "pk": 79, - "fields": { - "name": "Can delete collection rule", - "content_type": 20, - "codename": "delete_collectionrule" - } - }, - { - "model": "auth.permission", - "pk": 80, - "fields": { - "name": "Can view collection rule", - "content_type": 20, - "codename": "view_collectionrule" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 1, - "fields": { - "app_label": "admin", - "model": "logentry" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 2, - "fields": { - "app_label": "auth", - "model": "permission" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 3, - "fields": { - "app_label": "auth", - "model": "group" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 4, - "fields": { - "app_label": "contenttypes", - "model": "contenttype" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 5, - "fields": { - "app_label": "sessions", - "model": "session" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 6, - "fields": { - "app_label": "django_celery_beat", - "model": "crontabschedule" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 7, - "fields": { - "app_label": "django_celery_beat", - "model": "intervalschedule" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 8, - "fields": { - "app_label": "django_celery_beat", - "model": "periodictask" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 9, - "fields": { - "app_label": "django_celery_beat", - "model": "periodictasks" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 10, - "fields": { - "app_label": "django_celery_beat", - "model": "solarschedule" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 11, - "fields": { - "app_label": "django_celery_beat", - "model": "clockedschedule" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 12, - "fields": { - "app_label": "registration", - "model": "registrationprofile" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 13, - "fields": { - "app_label": "registration", - "model": "supervisedregistrationprofile" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 14, - "fields": { - "app_label": "axes", - "model": "accessattempt" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 15, - "fields": { - "app_label": "axes", - "model": "accesslog" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 16, - "fields": { - "app_label": "axes", - "model": "accessfailurelog" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 17, - "fields": { - "app_label": "accounts", - "model": "user" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 18, - "fields": { - "app_label": "core", - "model": "post" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 19, - "fields": { - "app_label": "core", - "model": "category" - } - }, - { - "model": "contenttypes.contenttype", - "pk": 20, - "fields": { - "app_label": "collection", - "model": "collectionrule" - } - }, - { - "model": "sessions.session", - "pk": "dfcr6pls3vg08u52oruobsronjpdb7n7", - "fields": { - "session_data": ".eJxVjMsKwjAURP8laylptEnqygoiBbGgdR1uem8fqC0kLSjivxuhmy7nnJn5MEdN50cHYzf0hp7QPdiW-aHv3xa0infNH0XV8GQrZmAaWzN5cqbDUIuXzEJ1p_4v4EU-mqOPspD2s1sMWvBtaMt1omTNUZPUgiuUyNeQIomY6oRQaRSCo002kApEqsJxqsVGCgvcAolwejkc82t5ycq8OJvsVhbmVBzzM9uObqLvD6HES58:1qEDEb:M-BTQk9zh0CLjYVFPmnJuYtawLqP4k_1CTGcPOCVAWE", - "expire_date": "2023-07-11T18:16:13.717Z" - } - }, - { - "model": "django_celery_beat.intervalschedule", - "pk": 1, - "fields": { - "every": 1, - "period": "hours" - } - }, - { - "model": "django_celery_beat.crontabschedule", - "pk": 1, - "fields": { - "minute": "0", - "hour": "4", - "day_of_week": "*", - "day_of_month": "*", - "month_of_year": "*", - "timezone": "UTC" - } - }, - { - "model": "django_celery_beat.periodictasks", - "pk": 1, - "fields": { - "last_update": "2023-06-27T18:17:21.935Z" - } - }, - { - "model": "django_celery_beat.periodictask", - "pk": 1, - "fields": { - "name": "celery.backend_cleanup", - "task": "celery.backend_cleanup", - "interval": null, - "crontab": 1, - "solar": null, - "clocked": null, - "args": "[]", - "kwargs": "{}", - "queue": null, - "exchange": null, - "routing_key": null, - "headers": "{}", - "priority": null, - "expires": null, - "expire_seconds": 43200, - "one_off": false, - "start_time": null, - "enabled": true, - "last_run_at": null, - "total_run_count": 0, - "date_changed": "2023-06-27T18:14:50.515Z", - "description": "" - } - }, - { - "model": "django_celery_beat.periodictask", - "pk": 2, - "fields": { - "name": "sonnyba871@gmail.com-feed", - "task": "FeedTask", - "interval": 1, - "crontab": null, - "solar": null, - "clocked": null, - "args": "[1]", - "kwargs": "{}", - "queue": null, - "exchange": null, - "routing_key": null, - "headers": "{}", - "priority": null, - "expires": null, - "expire_seconds": null, - "one_off": false, - "start_time": null, - "enabled": true, - "last_run_at": null, - "total_run_count": 0, - "date_changed": "2023-06-27T18:17:21.936Z", - "description": "" - } - }, - { - "model": "registration.registrationprofile", - "pk": 1, - "fields": { - "user": 1, - "activation_key": "81f717cdd9531ff0c176a877c6d1c883b0020769872bd75d257f3cf14e2835e9", - "activated": true - } - }, - { - "model": "accounts.user", - "pk": 1, - "fields": { - "password": "pbkdf2_sha256$260000$NhL22c7aKG06wXrw4Kalke$G4iFkzIbUWP8kH+TrGpEud54CwpDsYQrzPMb7Fl/5cE=", - "last_login": "2023-06-27T18:16:13.715Z", - "is_superuser": true, - "first_name": "", - "last_name": "", - "is_staff": true, - "is_active": true, - "date_joined": "2023-06-27T18:15:53.710Z", - "email": "sonnyba871@gmail.com", - "reddit_refresh_token": null, - "reddit_access_token": null, - "auto_mark_read": true, - "groups": [], - "user_permissions": [] - } - }, - { - "model": "core.category", - "pk": 1, - "fields": { - "created": "2023-06-27T18:17:07.592Z", - "modified": "2023-06-27T18:17:07.595Z", - "name": "News", - "user": 1 - } - }, - { - "model": "collection.collectionrule", - "pk": 1, - "fields": { - "created": "2023-06-27T18:17:21.888Z", - "modified": "2023-06-27T18:17:21.929Z", - "name": "Nu.nl", - "type": "feed", - "url": "https://www.nu.nl/rss", - "website_url": null, - "favicon": null, - "timezone": "UTC", - "category": 1, - "last_run": null, - "succeeded": false, - "error": null, - "enabled": true, - "user": 1, - "reddit_allow_nfsw": false, - "reddit_allow_spoiler": false, - "reddit_allow_viewed": true, - "reddit_upvotes_min": 0, - "reddit_downvotes_max": null, - "reddit_comments_min": 0, - "screen_name": null - } - }, - { - "model": "collection.collectionrule", - "pk": 2, - "fields": { - "created": "2023-06-27T18:17:34.057Z", - "modified": "2023-06-27T18:17:34.060Z", - "name": "Telegraaf", - "type": "feed", - "url": "https://www.telegraaf.nl/nieuws/rss", - "website_url": null, - "favicon": null, - "timezone": "UTC", - "category": 1, - "last_run": null, - "succeeded": false, - "error": null, - "enabled": true, - "user": 1, - "reddit_allow_nfsw": false, - "reddit_allow_spoiler": false, - "reddit_allow_viewed": true, - "reddit_upvotes_min": 0, - "reddit_downvotes_max": null, - "reddit_comments_min": 0, - "screen_name": null - } - } -] diff --git a/src/newsreader/js/components/Messages.js b/src/newsreader/js/components/Messages.js index dd3b2f8..3fed5de 100644 --- a/src/newsreader/js/components/Messages.js +++ b/src/newsreader/js/components/Messages.js @@ -3,13 +3,15 @@ import React from 'react'; class Messages extends React.Component { state = { messages: this.props.messages }; - close = index => { + close = ::this.close; + + close(index) { const newMessages = this.state.messages.filter((message, currentIndex) => { return currentIndex != index; }); this.setState({ messages: newMessages }); - }; + } render() { const messages = this.state.messages.map((message, index) => { diff --git a/src/newsreader/js/components/NavList.js b/src/newsreader/js/components/NavList.js deleted file mode 100644 index 172ddfe..0000000 --- a/src/newsreader/js/components/NavList.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; - -class NavList extends React.Component { - render() { - const entries = Object.entries(this.props.navLinks); - const links = entries.map(([name, link], index) => { - return ( -
  • - {name} -
  • - ); - }); - - const className = this.props.includeBorder - ? 'nav-list nav-list--bordered' - : 'nav-list'; - - return
      {links}
    ; - } -} - -export default NavList; diff --git a/src/newsreader/js/components/Selector.js b/src/newsreader/js/components/Selector.js index 8933a59..8b701f5 100644 --- a/src/newsreader/js/components/Selector.js +++ b/src/newsreader/js/components/Selector.js @@ -1,4 +1,6 @@ class Selector { + onClick = ::this.onClick; + inputs = []; constructor() { @@ -9,13 +11,13 @@ class Selector { selectAllInput.onchange = this.onClick; } - onClick = e => { + onClick(e) { const targetValue = e.target.checked; this.inputs.forEach(input => { input.checked = targetValue; }); - }; + } } export default Selector; diff --git a/src/newsreader/js/components/Sidebar.js b/src/newsreader/js/components/Sidebar.js deleted file mode 100644 index 1bf45ab..0000000 --- a/src/newsreader/js/components/Sidebar.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; - -import NavList from './NavList.js'; - -// TODO: show empty category message -class Sidebar extends React.Component { - render() { - return ( -
    -
    - - - {this.props.children} -
    - -
    - ); - } -} - -export default Sidebar; diff --git a/src/newsreader/js/index.js b/src/newsreader/js/index.js index 0cb4335..ccb9553 100644 --- a/src/newsreader/js/index.js +++ b/src/newsreader/js/index.js @@ -2,4 +2,3 @@ import './lib/index.js'; import './pages/homepage/index.js'; import './pages/categories/index.js'; import './pages/rules/index.js'; -import './pages/default/index.js'; diff --git a/src/newsreader/js/lib/theme.js b/src/newsreader/js/lib/theme.js index 7d89c5f..ee7ef48 100644 --- a/src/newsreader/js/lib/theme.js +++ b/src/newsreader/js/lib/theme.js @@ -1,8 +1,8 @@ -const isCSSVariablesSupported = () => { +function isCSSVariablesSupported() { return window.CSS && window.CSS.supports('color', 'var(--fake-color'); -}; +} -const changeTheme = event => { +function changeTheme(e) { const currentPref = sessionStorage.getItem('t-dark'); const isDark = currentPref && currentPref === 'true' ? true : false; @@ -14,12 +14,12 @@ const changeTheme = event => { try { sessionStorage.setItem('t-dark', !isDark); - } catch (error) { + } catch (e) { // do nothing. } -}; +} -const getThemePreference = () => { +function prefersDarkTheme() { try { const currentPref = sessionStorage.getItem('t-dark'); @@ -33,12 +33,12 @@ const getThemePreference = () => { } else { return false; } - } catch (error) { + } catch (e) { return false; } -}; +} -const toggleDarkTheme = isDark => { +function toggleDarkTheme(isDark) { if (isDark) { document.documentElement.classList.add('dark-theme'); } else { @@ -47,30 +47,30 @@ const toggleDarkTheme = isDark => { try { sessionStorage.setItem('t-dark', isDark); - } catch (error) { + } catch (e) { // do nothing. } -}; +} -const initThemeSelector = () => { +function initThemeSelector() { const themeButton = document.getElementsByClassName('theme-switcher')[0]; - const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)'); + const mqPrefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)'); - if (getThemePreference()) { + if (prefersDarkTheme()) { toggleDarkTheme(true); } themeButton.addEventListener('click', changeTheme); - prefersDarkTheme.addListener(mediaQuery => { - toggleDarkTheme(mediaQuery.matches); + mqPrefersDarkTheme.addListener(mq => { + toggleDarkTheme(mq.matches); }); -}; +} -const init = () => { +function init() { if (isCSSVariablesSupported()) { initThemeSelector(); } -}; +} init(); diff --git a/src/newsreader/js/pages/categories/App.js b/src/newsreader/js/pages/categories/App.js index db81a73..a035b46 100644 --- a/src/newsreader/js/pages/categories/App.js +++ b/src/newsreader/js/pages/categories/App.js @@ -6,9 +6,12 @@ import Card from '../../components/Card.js'; import CategoryCard from './components/CategoryCard.js'; import CategoryModal from './components/CategoryModal.js'; import Messages from '../../components/Messages.js'; -import Sidebar from '../../components/Sidebar.js'; class App extends React.Component { + selectCategory = ::this.selectCategory; + deselectCategory = ::this.deselectCategory; + deleteCategory = ::this.deleteCategory; + constructor(props) { super(props); @@ -20,15 +23,15 @@ class App extends React.Component { }; } - selectCategory = categoryId => { + selectCategory(categoryId) { this.setState({ selectedCategoryId: categoryId }); - }; + } - deselectCategory = () => { + deselectCategory() { this.setState({ selectedCategoryId: null }); - }; + } - deleteCategory = categoryId => { + deleteCategory(categoryId) { const url = `/api/categories/${categoryId}/`; const options = { method: 'DELETE', @@ -56,7 +59,7 @@ class App extends React.Component { text: 'Unable to remove category, try again later', }; return this.setState({ selectedCategoryId: null, message: message }); - }; + } render() { const { categories } = this.state; @@ -87,19 +90,15 @@ class App extends React.Component { return ( <> {this.state.message && } - - -
    - - {cards} - {selectedCategory && ( - - )} -
    + + {cards} + {selectedCategory && ( + + )} ); } diff --git a/src/newsreader/js/pages/categories/index.js b/src/newsreader/js/pages/categories/index.js index 77d6940..791fdbd 100644 --- a/src/newsreader/js/pages/categories/index.js +++ b/src/newsreader/js/pages/categories/index.js @@ -12,15 +12,11 @@ if (page) { let createUrl = document.getElementById('createUrl').textContent; let updateUrl = document.getElementById('updateUrl').textContent; - let linkScript = document.getElementById('Links'); - let navLinks = JSON.parse(linkScript.textContent); - ReactDOM.render( , page ); diff --git a/src/newsreader/js/pages/default/index.js b/src/newsreader/js/pages/default/index.js deleted file mode 100644 index 98083a5..0000000 --- a/src/newsreader/js/pages/default/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import Sidebar from '../../components/Sidebar'; - -const mainElements = [...document.getElementsByClassName('main')]; -const mainElement = mainElements.find(element => element.dataset.renderSidebar); - -if (mainElement) { - let linkScript = document.getElementById('Links'); - let navLinks = JSON.parse(linkScript.textContent); - - ReactDOM.render( - ReactDOM.createPortal(, mainElement), - document.createElement('div') - ); -} diff --git a/src/newsreader/js/pages/homepage/App.js b/src/newsreader/js/pages/homepage/App.js index e840407..01ca773 100644 --- a/src/newsreader/js/pages/homepage/App.js +++ b/src/newsreader/js/pages/homepage/App.js @@ -4,25 +4,13 @@ import { connect } from 'react-redux'; import { isEqual } from 'lodash'; import { fetchCategories } from './actions/categories'; -import { filterPosts } from './components/postlist/filters.js'; -import ScrollTop from './components/ScrollTop.js'; -import HomepageSidebar from './components/sidebar/Sidebar.js'; +import Sidebar from './components/sidebar/Sidebar.js'; import PostList from './components/postlist/PostList.js'; import PostModal from './components/PostModal.js'; import Messages from '../../components/Messages.js'; class App extends React.Component { - state = { postListNode: null }; - - constructor(props) { - super(props); - - this.postListRef = node => { - this.setState({ postListNode: node }); - }; - } - componentDidMount() { this.props.fetchCategories(); } @@ -30,12 +18,12 @@ class App extends React.Component { render() { return ( <> - + {!isEqual(this.props.post, {}) && ( @@ -45,14 +33,14 @@ class App extends React.Component { category={this.props.category} selectedType={this.props.selectedType} feedUrl={this.props.feedUrl} + subredditUrl={this.props.subredditUrl} + timelineUrl={this.props.timelineUrl} categoriesUrl={this.props.categoriesUrl} timezone={this.props.timezone} autoMarking={this.props.autoMarking} /> )} - - {this.props.error && ( )} @@ -63,10 +51,9 @@ class App extends React.Component { const mapStateToProps = state => { const { error } = state.error; - const postsByType = filterPosts(state); if (!isEqual(state.selected.post, {})) { - const ruleId = state.selected.post.rule.id; + const ruleId = state.selected.post.rule; const rule = state.rules.items[ruleId]; const category = state.categories.items[rule.category]; @@ -77,11 +64,10 @@ const mapStateToProps = state => { rule, post: state.selected.post, selectedType: state.selected.item.type, - postsByType: postsByType, }; } - return { error, post: state.selected.post, postsByType: postsByType }; + return { error, post: state.selected.post }; }; const mapDispatchToProps = dispatch => ({ diff --git a/src/newsreader/js/pages/homepage/actions/posts.js b/src/newsreader/js/pages/homepage/actions/posts.js index 68ebe32..6a0cd7a 100644 --- a/src/newsreader/js/pages/homepage/actions/posts.js +++ b/src/newsreader/js/pages/homepage/actions/posts.js @@ -124,10 +124,10 @@ export const fetchPostsBySection = (section, next = false) => { switch (section.type) { case RULE_TYPE: - url = next ? next : `/api/rules/${section.id}/posts/`; + url = next ? next : `/api/rules/${section.id}/posts/?read=false`; break; case CATEGORY_TYPE: - url = next ? next : `/api/categories/${section.id}/posts/`; + url = next ? next : `/api/categories/${section.id}/posts/?read=false`; break; } diff --git a/src/newsreader/js/pages/homepage/components/PostModal.js b/src/newsreader/js/pages/homepage/components/PostModal.js index e319e10..9746aee 100644 --- a/src/newsreader/js/pages/homepage/components/PostModal.js +++ b/src/newsreader/js/pages/homepage/components/PostModal.js @@ -3,10 +3,18 @@ import { connect } from 'react-redux'; import Cookies from 'js-cookie'; import { unSelectPost, markPostRead, toggleSaved } from '../actions/posts.js'; -import { SAVED_TYPE } from '../constants.js'; +import { + CATEGORY_TYPE, + RULE_TYPE, + SAVED_TYPE, + FEED, + SUBREDDIT, + TWITTER_TIMELINE, +} from '../constants.js'; import { formatDatetime } from '../../../utils.js'; class PostModal extends React.Component { + modalListener = ::this.modalListener; readTimer = null; componentDidMount() { @@ -31,13 +39,13 @@ class PostModal extends React.Component { window.removeEventListener('click', this.modalListener); } - modalListener = e => { + modalListener(e) { const targetClassName = e.target.className; if (this.props.post && targetClassName == 'modal post-modal') { this.props.unSelectPost(); } - }; + } render() { const post = this.props.post; @@ -46,13 +54,17 @@ class PostModal extends React.Component { const titleClassName = post.read ? 'post__title post__title--read' : 'post__title'; const readButtonDisabled = post.read || this.props.isUpdating || this.props.selectedType === SAVED_TYPE; - const savedIconClass = post.saved - ? 'post__save post__save--saved saved-icon saved-icon--saved' - : 'post__save saved-icon'; + const savedIconClass = post.saved ? 'saved-icon saved-icon--saved' : 'saved-icon'; let ruleUrl = ''; switch (this.props.rule.type) { + case SUBREDDIT: + ruleUrl = `${this.props.subredditUrl}/${this.props.rule.id}/`; + break; + case TWITTER_TIMELINE: + ruleUrl = `${this.props.timelineUrl}/${this.props.rule.id}/`; + break; default: ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`; break; @@ -61,67 +73,59 @@ class PostModal extends React.Component { return (
    -
    -
    -
    - - -
    -
    -

    {`${post.title} `}

    -
    -
    - {publicationDate} - {post.author && {post.author}} -
    - -
    - {this.props.category && ( - - - {this.props.category.name} - - - )} - - - {this.props.rule.name} - - +
    +
    + + +
    +
    +

    {`${post.title} `}

    +
    + + {publicationDate} {this.props.timezone} + + {post.author && {post.author}} + {this.props.category && ( + - + {this.props.category.name} - this.props.toggleSaved(post, token)} - /> -
    -
    + + )} + + + {this.props.rule.name} + + + + + + this.props.toggleSaved(post, token)} + />
    diff --git a/src/newsreader/js/pages/homepage/components/ScrollTop.js b/src/newsreader/js/pages/homepage/components/ScrollTop.js deleted file mode 100644 index 9d88c39..0000000 --- a/src/newsreader/js/pages/homepage/components/ScrollTop.js +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -export default class ScrollTop extends React.Component { - state = { - listenerAttached: false, - showTop: false, - showBottom: false, - }; - - componentDidUpdate() { - if (this.props.postListNode && !this.state.listenerAttached) { - this.props.postListNode.addEventListener('scroll', this.scrollListener); - - this.setState({ listenerAttached: true }); - } - } - - scrollListener = () => { - const postList = this.props.postListNode; - const elementEnd = - postList.scrollTop + postList.offsetHeight >= postList.scrollHeight; - - this.setState({ - showTop: postList.scrollTop > window.innerHeight, - showBottom: !elementEnd, - }); - }; - - render() { - const postList = this.props.postListNode; - - return ( - postList && ( -
    - {this.state.showTop && ( - postList.scroll({ top: 0 })} - /> - )} - - {this.state.showBottom && ( - postList.scroll({ top: postList.scrollHeight })} - /> - )} -
    - ) - ); - } -} diff --git a/src/newsreader/js/pages/homepage/components/postlist/PostItem.js b/src/newsreader/js/pages/homepage/components/postlist/PostItem.js index 495f87b..83833f5 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/PostItem.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostItem.js @@ -2,14 +2,21 @@ import React from 'react'; import { connect } from 'react-redux'; import Cookies from 'js-cookie'; -import { CATEGORY_TYPE, SAVED_TYPE } from '../../constants.js'; +import { + CATEGORY_TYPE, + RULE_TYPE, + SAVED_TYPE, + FEED, + SUBREDDIT, + TWITTER_TIMELINE, +} from '../../constants.js'; import { selectPost, toggleSaved } from '../../actions/posts.js'; import { formatDatetime } from '../../../../utils.js'; class PostItem extends React.Component { render() { const rule = { ...this.props.post.rule }; - const post = { ...this.props.post }; + const post = { ...this.props.post, rule: rule.id }; const token = Cookies.get('csrftoken'); const publicationDate = formatDatetime(post.publicationDate); @@ -18,10 +25,17 @@ class PostItem extends React.Component { : 'posts__header'; const savedIconClass = post.saved ? 'saved-icon saved-icon--saved' : 'saved-icon'; - const ruleUrl = `${this.props.feedUrl}/${rule.id}/`; + let ruleUrl = ''; + if (rule.type === SUBREDDIT) { + ruleUrl = `${this.props.subredditUrl}/${rule.id}/`; + } else if (rule.type === TWITTER_TIMELINE) { + ruleUrl = `${this.props.timelineUrl}/${rule.id}/`; + } else { + ruleUrl = `${this.props.feedUrl}/${rule.id}/`; + } return ( -
  • +
  • - {publicationDate} {post.author && `By ${post.author}`} + {publicationDate} {this.props.timezone} {post.author && `By ${post.author}`} {[CATEGORY_TYPE, SAVED_TYPE].includes(this.props.selected.type) && ( diff --git a/src/newsreader/js/pages/homepage/components/postlist/PostList.js b/src/newsreader/js/pages/homepage/components/postlist/PostList.js index 12e381e..82617f8 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/PostList.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostList.js @@ -4,46 +4,34 @@ import { isEqual } from 'lodash'; import { fetchPostsBySection, fetchSavedPosts } from '../../actions/posts.js'; import { SAVED_TYPE } from '../../constants.js'; +import { filterPosts } from './filters.js'; import LoadingIndicator from '../../../../components/LoadingIndicator.js'; import PostItem from './PostItem.js'; class PostList extends React.Component { - lastPostRef = null; - observer = null; + checkScrollHeight = ::this.checkScrollHeight; - constructor(props) { - super(props); - - this.lastPostRef = React.createRef(); - - const observerOptions = { root: null, rootMargin: '0px', threshold: 0 }; - this.observer = new IntersectionObserver(this.handleIntersect, observerOptions); - } - - componentDidUpdate() { - if (this.lastPostRef.current && !this.props.isFetching) { - this.observer.observe(this.lastPostRef.current); - } + componentDidMount() { + window.addEventListener('scroll', this.checkScrollHeight); } componentWillUnmount() { - this.observer.disconnect(); + window.removeEventListener('scroll', this.checkScrollHeight); } - handleIntersect = entries => { - entries.every(entry => { - if (entry.isIntersecting) { - this.observer.unobserve(entry.target); + checkScrollHeight(e) { + const currentHeight = window.scrollY + window.innerHeight; + const totalHeight = document.body.offsetHeight; - if (this.props.next && !this.props.lastReached) { - this.paginate(); - } + const currentPercentage = (currentHeight / totalHeight) * 100; - return false; + if (this.props.next && !this.props.lastReached) { + if (currentPercentage > 60 && !this.props.isFetching) { + this.paginate(); } - }); - }; + } + } paginate() { if (this.props.selected.type === SAVED_TYPE) { @@ -54,21 +42,18 @@ class PostList extends React.Component { } render() { - const isLastItem = this.props.postsByType.toReversed().find(item => !item.read); - const postItems = this.props.postsByType.map((item, index) => { - const defaultProps = { - key: index, - post: item, - selected: this.props.selected, - feedUrl: this.props.feedUrl, - }; - - if (isLastItem?.id === item.id) { - return ; - } else { - return ; - } + return ( + + ); }); if (isEqual(this.props.selected, {})) { @@ -92,7 +77,7 @@ class PostList extends React.Component { ); } else { return ( -
    +
      {postItems}
    {this.props.isFetching && }
    @@ -103,6 +88,7 @@ class PostList extends React.Component { const mapStateToProps = state => ({ isFetching: state.posts.isFetching, + postsByType: filterPosts(state), next: state.selected.next, lastReached: state.selected.lastReached, selected: state.selected.item, @@ -113,6 +99,4 @@ const mapDispatchToProps = dispatch => ({ fetchSavedPosts: (next = false) => dispatch(fetchSavedPosts(next)), }); -export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })( - PostList -); +export default connect(mapStateToProps, mapDispatchToProps)(PostList); diff --git a/src/newsreader/js/pages/homepage/components/postlist/filters.js b/src/newsreader/js/pages/homepage/components/postlist/filters.js index b3dd16a..8439fc9 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/filters.js +++ b/src/newsreader/js/pages/homepage/components/postlist/filters.js @@ -4,10 +4,31 @@ const isEmpty = (object = {}) => { return Object.keys(object).length === 0; }; +const sortOrdering = (firstPost, secondPost) => { + const dateOrdering = + new Date(firstPost.publicationDate) < new Date(secondPost.publicationDate); + + if (firstPost.read && !secondPost.read) { + return 1; + } else if (secondPost.read && !firstPost.read) { + return -1; + } + + return dateOrdering; +}; + +const savedOrdering = (firstPost, secondPost) => { + return new Date(firstPost.publicationDate) < new Date(secondPost.publicationDate); +}; + export const filterPostsByRule = (rule = {}, posts = []) => { - return posts.filter(post => { - return post.rule.id === rule.id; + const filteredPosts = posts.filter(post => { + return post.rule === rule.id; }); + + const filteredData = filteredPosts.map(post => ({ ...post, rule: { ...rule } })); + + return filteredData.sort(sortOrdering); }; export const filterPostsByCategory = (category = {}, rules = [], posts = []) => { @@ -15,13 +36,24 @@ export const filterPostsByCategory = (category = {}, rules = [], posts = []) => return rule.category === category.id; }); - const ruleIds = filteredRules.map(rule => rule.id); + const filteredData = filteredRules.map(rule => { + const filteredPosts = posts.filter(post => { + return post.rule === rule.id; + }); - return [...posts].filter(post => ruleIds.includes(post.rule.id)); + return filteredPosts.map(post => ({ ...post, rule: { ...rule } })); + }); + + const sortedPosts = [...filteredData.flat()].sort(sortOrdering); + + return sortedPosts; }; export const filterPostsBySaved = (rules = [], posts = []) => { - return [...posts].filter(post => post.saved); + const filteredPosts = posts.filter(post => post.saved); + return filteredPosts + .map(post => ({ ...post, rule: { ...rules.find(rule => rule.id === post.rule) } })) + .sort(savedOrdering); }; export const filterPosts = state => { diff --git a/src/newsreader/js/pages/homepage/components/sidebar/CategoryItem.js b/src/newsreader/js/pages/homepage/components/sidebar/CategoryItem.js index 9fcbeb7..5d384db 100644 --- a/src/newsreader/js/pages/homepage/components/sidebar/CategoryItem.js +++ b/src/newsreader/js/pages/homepage/components/sidebar/CategoryItem.js @@ -1,10 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; +import { isEqual } from 'lodash'; import { CATEGORY_TYPE } from '../../constants.js'; import { selectCategory, fetchCategory } from '../../actions/categories.js'; import { fetchPostsBySection } from '../../actions/posts.js'; - import { isSelected } from './functions.js'; import RuleItem from './RuleItem.js'; diff --git a/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js b/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js index 3083b96..3711c85 100644 --- a/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js +++ b/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js @@ -5,13 +5,15 @@ import Cookies from 'js-cookie'; import { markRead } from '../../actions/selected.js'; class ReadButton extends React.Component { - markSelectedRead = () => { + markSelectedRead = ::this.markSelectedRead; + + markSelectedRead() { const token = Cookies.get('csrftoken'); if (this.props.selected.unread > 0) { this.props.markRead({ ...this.props.selected }, token); } - }; + } render() { return ( diff --git a/src/newsreader/js/pages/homepage/components/sidebar/RuleItem.js b/src/newsreader/js/pages/homepage/components/sidebar/RuleItem.js index a9a9110..11289c5 100644 --- a/src/newsreader/js/pages/homepage/components/sidebar/RuleItem.js +++ b/src/newsreader/js/pages/homepage/components/sidebar/RuleItem.js @@ -1,10 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; +import { isEqual } from 'lodash'; import { RULE_TYPE } from '../../constants.js'; import { selectRule, fetchRule } from '../../actions/rules.js'; import { fetchPostsBySection } from '../../actions/posts.js'; - import { isSelected } from './functions.js'; class RuleItem extends React.Component { diff --git a/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js b/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js index 4c6cfad..88a69f2 100644 --- a/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js +++ b/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js @@ -1,17 +1,17 @@ import React from 'react'; import { connect } from 'react-redux'; +import { isEqual } from 'lodash'; + +import { filterCategories, filterRules } from './filters.js'; -import Sidebar from '../../../../components/Sidebar.js'; -import LoadingIndicator from '../../../../components/LoadingIndicator.js'; import { CATEGORY_TYPE, RULE_TYPE } from '../../constants.js'; - +import LoadingIndicator from '../../../../components/LoadingIndicator.js'; import CategoryItem from './CategoryItem.js'; import SavedItem from './SavedItem.js'; import ReadButton from './ReadButton.js'; -import { filterCategories, filterRules } from './filters.js'; - -class HomepageSidebar extends React.Component { +// TODO: show empty category message +class Sidebar extends React.Component { render() { const categoryItems = this.props.categories.items.map(category => { const rules = this.props.rules.items.filter(rule => { @@ -33,18 +33,18 @@ class HomepageSidebar extends React.Component { [CATEGORY_TYPE, RULE_TYPE].includes(this.props.selected.item.type); return ( - +
    {(this.props.categories.isFetching || this.props.rules.isFetching) && ( )} -
      +
        {categoryItems}
      {showReadButton && } - +
    ); } } @@ -55,4 +55,4 @@ const mapStateToProps = state => ({ selected: state.selected, }); -export default connect(mapStateToProps)(HomepageSidebar); +export default connect(mapStateToProps)(Sidebar); diff --git a/src/newsreader/js/pages/homepage/constants.js b/src/newsreader/js/pages/homepage/constants.js index 31524f7..0f5629b 100644 --- a/src/newsreader/js/pages/homepage/constants.js +++ b/src/newsreader/js/pages/homepage/constants.js @@ -2,4 +2,6 @@ export const RULE_TYPE = 'RULE'; export const CATEGORY_TYPE = 'CATEGORY'; export const SAVED_TYPE = 'SAVED'; +export const SUBREDDIT = 'subreddit'; export const FEED = 'feed'; +export const TWITTER_TIMELINE = 'twitter_timeline'; diff --git a/src/newsreader/js/pages/homepage/index.js b/src/newsreader/js/pages/homepage/index.js index 9b56ebd..86cdd09 100644 --- a/src/newsreader/js/pages/homepage/index.js +++ b/src/newsreader/js/pages/homepage/index.js @@ -12,18 +12,17 @@ if (page) { const store = configureStore(); const settings = JSON.parse(document.getElementById('homepageSettings').textContent); - const { feedUrl, categoriesUrl } = settings; - - const navLinks = JSON.parse(document.getElementById('Links').textContent); + const { feedUrl, subredditUrl, timelineUrl, categoriesUrl } = settings; const app = ( ); diff --git a/src/newsreader/js/pages/homepage/reducers/categories.js b/src/newsreader/js/pages/homepage/reducers/categories.js index 6a1f253..612b98f 100644 --- a/src/newsreader/js/pages/homepage/reducers/categories.js +++ b/src/newsreader/js/pages/homepage/reducers/categories.js @@ -39,14 +39,14 @@ export const categories = (state = { ...defaultState }, action) => { case REQUEST_CATEGORY: return { ...state, isFetching: true }; case MARK_POST_READ: - let postCategory = {}; + let category = {}; switch (action.section.type) { case CATEGORY_TYPE: - postCategory = { ...state.items[action.section.id] }; + category = { ...state.items[action.section.id] }; break; case RULE_TYPE: - postCategory = { ...state.items[action.section.category] }; + category = { ...state.items[action.section.category] }; break; } @@ -54,33 +54,33 @@ export const categories = (state = { ...defaultState }, action) => { ...state, items: { ...state.items, - [postCategory.id]: { ...postCategory, unread: postCategory.unread - 1 }, + [category.id]: { ...category, unread: category.unread - 1 }, }, }; case MARK_SECTION_READ: - let sectionCategory = {}; + category = {}; switch (action.section.type) { case CATEGORY_TYPE: - sectionCategory = { ...state.items[action.section.id] }; + category = { ...state.items[action.section.id] }; return { ...state, items: { ...state.items, - [sectionCategory.id]: { ...sectionCategory, unread: 0 }, + [category.id]: { ...category, unread: 0 }, }, }; case RULE_TYPE: - sectionCategory = { ...state.items[action.section.category] }; + category = { ...state.items[action.section.category] }; return { ...state, items: { ...state.items, - [sectionCategory.id]: { - ...sectionCategory, - unread: sectionCategory.unread - action.section.unread, + [category.id]: { + ...category, + unread: category.unread - action.section.unread, }, }, }; diff --git a/src/newsreader/js/pages/homepage/reducers/posts.js b/src/newsreader/js/pages/homepage/reducers/posts.js index 2c1ff86..dd795a0 100644 --- a/src/newsreader/js/pages/homepage/reducers/posts.js +++ b/src/newsreader/js/pages/homepage/reducers/posts.js @@ -1,3 +1,5 @@ +import { isEqual } from 'lodash'; + import { objectsFromArray } from '../../../utils.js'; import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js'; @@ -10,83 +12,58 @@ import { TOGGLING_POST, TOGGLED_POST, } from '../actions/posts.js'; +import { SELECT_CATEGORY } from '../actions/categories.js'; +import { SELECT_RULE } from '../actions/rules.js'; import { MARK_SECTION_READ } from '../actions/selected.js'; -const sortOrdering = (firstPost, secondPost) => { - const dateOrdering = - new Date(firstPost.publicationDate) < new Date(secondPost.publicationDate); - - if (firstPost.read && !secondPost.read) { - return 1; - } else if (secondPost.read && !firstPost.read) { - return -1; - } - - return dateOrdering; -}; - -const defaultState = { items: [], isFetching: false, isUpdating: false }; +const defaultState = { items: {}, isFetching: false, isUpdating: false }; export const posts = (state = { ...defaultState }, action) => { switch (action.type) { case REQUEST_POSTS: return { ...state, isFetching: true }; case RECEIVE_POST: - const postIndex = state.items.findIndex(post => post.id === action.post.id); - let newItems = [...state.items]; - - if (postIndex >= 0) { - newItems.splice(postIndex, 1, { ...action.post }); - } else { - newItems = [...state.items, { ...action.post }]; - } - - newItems.sort(sortOrdering); - return { ...state, - items: newItems, + items: { ...state.items, [action.post.id]: { ...action.post } }, }; case RECEIVE_POSTS: - const receivedPosts = objectsFromArray(action.posts, 'id'); - - let mergedPosts = { ...objectsFromArray(state.items, 'id'), ...receivedPosts }; - let sortedPosts = Object.values(mergedPosts).sort(sortOrdering); + const receivedItems = objectsFromArray(action.posts, 'id'); return { ...state, isFetching: false, - items: sortedPosts, + items: { ...state.items, ...receivedItems }, }; case MARK_SECTION_READ: - let posts = []; + const updatedPosts = {}; + let relatedPosts = []; switch (action.section.type) { case CATEGORY_TYPE: - posts = [...state.items]; - - posts.forEach(post => { - if (!(post.rule.id in { ...action.section.rules })) return; - - post.read = true; + relatedPosts = Object.values({ ...state.items }).filter(post => { + return post.rule in { ...action.section.rules }; }); break; case RULE_TYPE: - posts = [...state.items]; - - posts.forEach(post => { - if (post.rule.id != action.section.id) return; - - post.read = true; + relatedPosts = Object.values({ ...state.items }).filter(post => { + return post.rule === action.section.id; }); break; } + relatedPosts.forEach(post => { + updatedPosts[post.id] = { ...post, read: true }; + }); + return { ...state, - items: posts, + items: { + ...state.items, + ...updatedPosts, + }, }; case MARKING_POST: return { ...state, isUpdating: true }; diff --git a/src/newsreader/js/tests/homepage/actions/post.test.js b/src/newsreader/js/tests/homepage/actions/post.test.js index 6fd5d3d..d30e549 100644 --- a/src/newsreader/js/tests/homepage/actions/post.test.js +++ b/src/newsreader/js/tests/homepage/actions/post.test.js @@ -304,10 +304,10 @@ describe('post actions', () => { type: constants.RULE_TYPE, }; - fetchMock.getOnce('/api/rules/4/posts/', { + fetchMock.getOnce('/api/rules/4/posts/?read=false', { body: { count: 2, - next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar', + next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar&read=false', previous: null, results: posts, }, @@ -325,7 +325,7 @@ describe('post actions', () => { { type: actions.REQUEST_POSTS }, { type: actions.RECEIVE_POSTS, - next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar', + next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar&read=false', posts, }, ]; @@ -373,10 +373,10 @@ describe('post actions', () => { type: constants.CATEGORY_TYPE, }; - fetchMock.getOnce('/api/categories/1/posts/', { + fetchMock.getOnce('/api/categories/1/posts/?read=false', { body: { count: 2, - next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar', + next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar&read=false', previous: null, results: posts, }, @@ -394,7 +394,7 @@ describe('post actions', () => { { type: actions.REQUEST_POSTS }, { type: actions.RECEIVE_POSTS, - next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar', + next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar&read=false', posts, }, ]; @@ -600,7 +600,7 @@ describe('post actions', () => { const errorMessage = 'Page not found'; - fetchMock.getOnce(`/api/rules/${rule.id}/posts/`, () => { + fetchMock.getOnce(`/api/rules/${rule.id}/posts/?read=false`, () => { throw new Error(errorMessage); }); diff --git a/src/newsreader/js/tests/homepage/reducers/post.test.js b/src/newsreader/js/tests/homepage/reducers/post.test.js index 59f0e36..adb8983 100644 --- a/src/newsreader/js/tests/homepage/reducers/post.test.js +++ b/src/newsreader/js/tests/homepage/reducers/post.test.js @@ -6,7 +6,7 @@ import * as actions from '../../../pages/homepage/actions/posts.js'; import * as selectedActions from '../../../pages/homepage/actions/selected.js'; import * as constants from '../../../pages/homepage/constants.js'; -const defaultState = { items: [], isFetching: false }; +const defaultState = { items: {}, isFetching: false }; describe('post actions', () => { it('should return state after requesting posts', () => { @@ -42,7 +42,7 @@ describe('post actions', () => { ...defaultState, isFetching: false, isUpdating: false, - items: [post], + items: { [post.id]: post }, }; expect(reducer(undefined, action)).toEqual(expectedState); @@ -81,15 +81,16 @@ describe('post actions', () => { const action = { type: actions.RECEIVE_POSTS, - next: 'https://durp.com/api/rules/4/posts/?page=2', + next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', posts, }; + const expectedPosts = objectsFromArray(posts, 'id'); const expectedState = { ...defaultState, isFetching: false, isUpdating: false, - items: posts, + items: expectedPosts, }; expect(reducer(undefined, action)).toEqual(expectedState); @@ -107,14 +108,7 @@ describe('post actions', () => { author: 'Kyle Orland', publicationDate: '2020-01-24T19:50:12Z', url: 'https://arstechnica.com/?p=1648607', - rule: { - id: 5, - name: 'Ars Technica', - url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml', - favicon: 'https://cdn.arstechnica.net/favicon.ico', - category: 9, - unread: 544, - }, + rule: 5, read: false, }, 2141: { @@ -126,14 +120,7 @@ describe('post actions', () => { author: 'WIRED', publicationDate: '2020-01-25T11:06:46Z', url: 'https://arstechnica.com/?p=1648757', - rule: { - id: 5, - name: 'Ars Technica', - url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml', - favicon: 'https://cdn.arstechnica.net/favicon.ico', - category: 9, - unread: 544, - }, + rule: 5, read: false, }, 4637: { @@ -145,9 +132,7 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T19:08:25Z', url: 'https://www.bbc.co.uk/news/world-asia-china-51299195', - rule: { - id: 4, - }, + rule: 4, read: false, saved: false, }, @@ -160,9 +145,7 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T18:27:56Z', url: 'https://www.bbc.co.uk/news/world-europe-51294305', - rule: { - id: 4, - }, + rule: 4, read: false, saved: false, }, @@ -182,17 +165,16 @@ describe('post actions', () => { section: { ...rule, type: constants.RULE_TYPE }, }; - const state = { ...defaultState, items: Object.values(posts) }; + const state = { ...defaultState, items: { ...posts } }; const expectedState = { ...defaultState, isFetching: false, - items: [ - { ...posts[2067], read: true }, - { ...posts[2141], read: true }, - { ...posts[4637] }, - { ...posts[4638] }, - ], + items: { + ...posts, + 2067: { ...posts[2067], read: true }, + 2141: { ...posts[2141], read: true }, + }, }; expect(reducer(state, action)).toEqual(expectedState); @@ -210,15 +192,7 @@ describe('post actions', () => { author: 'Kyle Orland', publicationDate: '2020-01-24T19:50:12Z', url: 'https://arstechnica.com/?p=1648607', - rule: { - id: 5, - name: 'BBC', - url: 'http://feeds.bbci.co.uk/news/world/rss.xml', - favicon: - 'https://m.files.bbci.co.uk/modules/bbc-morph-news-waf-page-meta/2.5.2/apple-touch-icon-57x57-precomposed.png', - category: 8, - unread: 632, - }, + rule: 5, read: false, saved: false, }, @@ -231,15 +205,7 @@ describe('post actions', () => { author: 'WIRED', publicationDate: '2020-01-25T11:06:46Z', url: 'https://arstechnica.com/?p=1648757', - rule: { - id: 5, - name: 'BBC', - url: 'http://feeds.bbci.co.uk/news/world/rss.xml', - favicon: - 'https://m.files.bbci.co.uk/modules/bbc-morph-news-waf-page-meta/2.5.2/apple-touch-icon-57x57-precomposed.png', - category: 8, - unread: 632, - }, + rule: 5, read: false, saved: false, }, @@ -252,15 +218,7 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T19:08:25Z', url: 'https://www.bbc.co.uk/news/world-asia-china-51299195', - rule: { - id: 4, - name: 'BBC', - url: 'http://feeds.bbci.co.uk/news/world/rss.xml', - favicon: - 'https://m.files.bbci.co.uk/modules/bbc-morph-news-waf-page-meta/2.5.2/apple-touch-icon-57x57-precomposed.png', - category: 8, - unread: 321, - }, + rule: 4, read: false, saved: false, }, @@ -273,15 +231,7 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T18:27:56Z', url: 'https://www.bbc.co.uk/news/world-europe-51294305', - rule: { - id: 4, - name: 'BBC', - url: 'http://feeds.bbci.co.uk/news/world/rss.xml', - favicon: - 'https://m.files.bbci.co.uk/modules/bbc-morph-news-waf-page-meta/2.5.2/apple-touch-icon-57x57-precomposed.png', - category: 8, - unread: 321, - }, + rule: 4, read: false, saved: false, }, @@ -295,9 +245,7 @@ describe('post actions', () => { publicationDate: '2020-01-29T19:03:01Z', url: 'https://tweakers.net/nieuws/162878/analyse-nintendo-verdiende-miljard-dollar-aan-mobiele-games.html', - rule: { - id: 7, - }, + rule: 7, read: false, saved: false, }, @@ -311,9 +259,7 @@ describe('post actions', () => { publicationDate: '2020-01-29T16:29:40Z', url: 'https://tweakers.net/nieuws/162870/samsung-kondigt-eerste-tablet-met-5g-aan.html', - rule: { - id: 7, - }, + rule: 7, read: false, saved: false, }, @@ -330,7 +276,7 @@ describe('post actions', () => { unread: 321, }, 5: { - id: 5, + id: 4, name: 'BBC', url: 'http://feeds.bbci.co.uk/news/world/rss.xml', favicon: @@ -355,19 +301,18 @@ describe('post actions', () => { }, }; - const state = { ...defaultState, items: Object.values(posts) }; + const state = { ...defaultState, items: { ...posts } }; const expectedState = { ...defaultState, isFetching: false, - items: [ - { ...posts[2067], read: true }, - { ...posts[2141], read: true }, - { ...posts[4589] }, - { ...posts[4594] }, - { ...posts[4637], read: true }, - { ...posts[4638], read: true }, - ], + items: { + ...posts, + 2067: { ...posts[2067], read: true }, + 2141: { ...posts[2141], read: true }, + 4637: { ...posts[4637], read: true }, + 4638: { ...posts[4638], read: true }, + }, }; expect(reducer(state, action)).toEqual(expectedState); diff --git a/src/newsreader/js/tests/homepage/reducers/selected.test.js b/src/newsreader/js/tests/homepage/reducers/selected.test.js index 0d1a7ae..40561a3 100644 --- a/src/newsreader/js/tests/homepage/reducers/selected.test.js +++ b/src/newsreader/js/tests/homepage/reducers/selected.test.js @@ -254,13 +254,13 @@ describe('selected reducer', () => { const action = { type: postActions.RECEIVE_POSTS, - next: 'https://durp.com/api/rules/4/posts/?page=2', + next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', posts, }; const expectedState = { ...defaultState, - next: 'https://durp.com/api/rules/4/posts/?page=2', + next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', lastReached: false, }; diff --git a/src/newsreader/news/collection/choices.py b/src/newsreader/news/collection/choices.py index 05b6eb3..612079c 100644 --- a/src/newsreader/news/collection/choices.py +++ b/src/newsreader/news/collection/choices.py @@ -4,3 +4,11 @@ from django.utils.translation import gettext as _ class RuleTypeChoices(TextChoices): feed = "feed", _("Feed") + subreddit = "subreddit", _("Subreddit") + twitter_timeline = "twitter_timeline", _("Twitter timeline") + + +class TwitterPostTypeChoices(TextChoices): + photo = "photo", _("Photo") + video = "video", _("Video") + animated_gif = "animated_gif", _("GIF") diff --git a/src/newsreader/news/collection/endpoints.py b/src/newsreader/news/collection/endpoints.py index c5c5580..623283d 100644 --- a/src/newsreader/news/collection/endpoints.py +++ b/src/newsreader/news/collection/endpoints.py @@ -1,5 +1,3 @@ -from django.db.models import Prefetch - from rest_framework import status from rest_framework.generics import ( GenericAPIView, @@ -9,9 +7,14 @@ from rest_framework.generics import ( ) from rest_framework.response import Response -from newsreader.core.pagination import CursorPagination +from newsreader.core.pagination import ( + CursorPagination, + LargeResultSetPagination, + ResultSetPagination, +) from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.serializers import RuleSerializer +from newsreader.news.core.filters import ReadFilter from newsreader.news.core.models import Post from newsreader.news.core.serializers import PostSerializer @@ -25,6 +28,7 @@ class NestedRuleView(ListAPIView): queryset = CollectionRule.objects.prefetch_related("posts").all() serializer_class = PostSerializer pagination_class = CursorPagination + filter_backends = [ReadFilter] def get_queryset(self): lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field @@ -33,9 +37,7 @@ class NestedRuleView(ListAPIView): # filtered on the user. filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} - prefetch = Prefetch("posts", queryset=Post.objects.filter(read=False)) - queryset = CollectionRule.objects.prefetch_related(prefetch) - rule = get_object_or_404(queryset, **filter_kwargs) + rule = get_object_or_404(self.queryset, **filter_kwargs) self.check_object_permissions(self.request, rule) return rule.posts.order_by("-publication_date") diff --git a/src/newsreader/news/collection/exceptions/__init__.py b/src/newsreader/news/collection/exceptions/__init__.py index 5d02b32..7f2c72d 100644 --- a/src/newsreader/news/collection/exceptions/__init__.py +++ b/src/newsreader/news/collection/exceptions/__init__.py @@ -15,20 +15,3 @@ from newsreader.news.collection.exceptions.stream import ( StreamTimeOutException, StreamTooManyException, ) - - -__all__ = [ - "BuilderDuplicateException", - "BuilderException", - "BuilderMissingDataException", - "BuilderParseException", - "BuilderSkippedException", - "StreamConnectionException", - "StreamDeniedException", - "StreamException", - "StreamForbiddenException", - "StreamNotFoundException", - "StreamParseException", - "StreamTimeOutException", - "StreamTooManyException", -] diff --git a/src/newsreader/news/collection/favicon.py b/src/newsreader/news/collection/favicon.py index 17d4045..1ca21e6 100644 --- a/src/newsreader/news/collection/favicon.py +++ b/src/newsreader/news/collection/favicon.py @@ -81,7 +81,7 @@ class FaviconBuilder(Builder): icons = set() for link in links: - if "href" not in link.attrs: + if not "href" in link.attrs: continue if "favicon" in link["href"]: diff --git a/src/newsreader/news/collection/feed.py b/src/newsreader/news/collection/feed.py index 87f7d85..368f53c 100644 --- a/src/newsreader/news/collection/feed.py +++ b/src/newsreader/news/collection/feed.py @@ -6,6 +6,8 @@ from datetime import timedelta from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.utils import timezone +import pytz + from feedparser import parse from newsreader.news.collection.base import ( @@ -16,6 +18,7 @@ from newsreader.news.collection.base import ( ) from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.exceptions import ( + StreamDeniedException, StreamException, StreamNotFoundException, StreamParseException, @@ -56,16 +59,17 @@ class FeedBuilder(PostBuilder): "published_parsed": "publication_date", "author": "author", } + tz = pytz.timezone(self.stream.rule.timezone) data = {"rule_id": self.stream.rule.pk} for field, model_field in field_mapping.items(): - if field not in entry: + if not field in entry: continue value = truncate_text(Post, model_field, entry[field]) if field == "published_parsed": - data[model_field] = build_publication_date(value) + data[model_field] = build_publication_date(value, tz) elif field == "summary": data[model_field] = self.sanitize_fragment(value) else: @@ -74,7 +78,7 @@ class FeedBuilder(PostBuilder): content_details = self.get_content_details(entry) # use content details key if it contains more information - if "body" not in data or len(data["body"]) < len(content_details): + if not "body" in data or len(data["body"]) < len(content_details): data["body"] = content_details return Post(**data) diff --git a/src/newsreader/news/collection/forms/__init__.py b/src/newsreader/news/collection/forms/__init__.py index d5ee1bd..88a51c7 100644 --- a/src/newsreader/news/collection/forms/__init__.py +++ b/src/newsreader/news/collection/forms/__init__.py @@ -1,9 +1,4 @@ from newsreader.news.collection.forms.feed import FeedForm, OPMLImportForm +from newsreader.news.collection.forms.reddit import SubRedditForm from newsreader.news.collection.forms.rules import CollectionRuleBulkForm - - -__all__ = [ - "FeedForm", - "OPMLImportForm", - "CollectionRuleBulkForm", -] +from newsreader.news.collection.forms.twitter import TwitterTimelineForm diff --git a/src/newsreader/news/collection/forms/feed.py b/src/newsreader/news/collection/forms/feed.py index 0b52c5f..4a22a2e 100644 --- a/src/newsreader/news/collection/forms/feed.py +++ b/src/newsreader/news/collection/forms/feed.py @@ -1,4 +1,7 @@ from django import forms +from django.utils.translation import gettext_lazy as _ + +import pytz from newsreader.core.forms import CheckboxInput from newsreader.news.collection.forms.base import CollectionRuleForm @@ -6,9 +9,16 @@ from newsreader.news.collection.models import CollectionRule class FeedForm(CollectionRuleForm): + timezone = forms.ChoiceField( + widget=forms.Select(attrs={"size": len(pytz.all_timezones)}), + choices=((timezone, timezone) for timezone in pytz.all_timezones), + help_text=_("The timezone which the feed uses"), + initial=pytz.utc, + ) + class Meta: model = CollectionRule - fields = ("name", "url", "favicon", "category") + fields = ("name", "url", "timezone", "favicon", "category") class OPMLImportForm(forms.Form): diff --git a/src/newsreader/news/collection/forms/reddit.py b/src/newsreader/news/collection/forms/reddit.py new file mode 100644 index 0000000..b4d1090 --- /dev/null +++ b/src/newsreader/news/collection/forms/reddit.py @@ -0,0 +1,60 @@ +from django import forms +from django.core.exceptions import ValidationError +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ + +import pytz + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.forms.base import CollectionRuleForm +from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.reddit import REDDIT_API_URL + + +def get_reddit_help_text(): + return mark_safe( + "Only subreddits are supported" + " see the 'listings' section in the reddit API docs." + " For example: https://oauth.reddit.com/r/aww" + ) + + +class SubRedditForm(CollectionRuleForm): + url = forms.URLField(max_length=1024, help_text=get_reddit_help_text) + + def clean_url(self): + url = self.cleaned_data["url"] + + if not url.startswith(REDDIT_API_URL): + raise ValidationError(_("This does not look like an Reddit API URL")) + + return url + + def save(self, commit=True): + instance = super().save(commit=False) + + instance.type = RuleTypeChoices.subreddit + instance.timezone = str(pytz.utc) + + if commit: + instance.save() + self.save_m2m() + + return instance + + class Meta: + model = CollectionRule + fields = ( + "name", + "url", + "favicon", + "category", + "reddit_allow_nfsw", + "reddit_allow_spoiler", + "reddit_allow_viewed", + "reddit_upvotes_min", + "reddit_downvotes_max", + "reddit_comments_min", + ) diff --git a/src/newsreader/news/collection/forms/twitter.py b/src/newsreader/news/collection/forms/twitter.py new file mode 100644 index 0000000..902652b --- /dev/null +++ b/src/newsreader/news/collection/forms/twitter.py @@ -0,0 +1,35 @@ +from django import forms +from django.utils.translation import gettext_lazy as _ + +import pytz + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.forms.base import CollectionRuleForm +from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.twitter import TWITTER_API_URL + + +class TwitterTimelineForm(CollectionRuleForm): + screen_name = forms.CharField( + max_length=255, + label=_("Twitter profile name"), + help_text=_("Profile name without hashtags"), + required=True, + ) + + def save(self, commit=True): + instance = super().save(commit=False) + + instance.type = RuleTypeChoices.twitter_timeline + instance.timezone = str(pytz.utc) + instance.url = f"{TWITTER_API_URL}/statuses/user_timeline.json?screen_name={instance.screen_name}&tweet_mode=extended" + + if commit: + instance.save() + self.save_m2m() + + return instance + + class Meta: + model = CollectionRule + fields = ("name", "screen_name", "favicon", "category") diff --git a/src/newsreader/news/collection/migrations/0001_initial.py b/src/newsreader/news/collection/migrations/0001_initial.py index d53ff0d..59910e5 100644 --- a/src/newsreader/news/collection/migrations/0001_initial.py +++ b/src/newsreader/news/collection/migrations/0001_initial.py @@ -6,6 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + initial = True dependencies = [] diff --git a/src/newsreader/news/collection/migrations/0002_auto_20190714_1036.py b/src/newsreader/news/collection/migrations/0002_auto_20190714_1036.py index 32a9ea6..6854c0b 100644 --- a/src/newsreader/news/collection/migrations/0002_auto_20190714_1036.py +++ b/src/newsreader/news/collection/migrations/0002_auto_20190714_1036.py @@ -7,6 +7,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + initial = True dependencies = [ diff --git a/src/newsreader/news/collection/migrations/0003_auto_20190714_1417.py b/src/newsreader/news/collection/migrations/0003_auto_20190714_1417.py index 945d94d..99f1018 100644 --- a/src/newsreader/news/collection/migrations/0003_auto_20190714_1417.py +++ b/src/newsreader/news/collection/migrations/0003_auto_20190714_1417.py @@ -5,6 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0002_auto_20190714_1036")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0004_auto_20190714_1422.py b/src/newsreader/news/collection/migrations/0004_auto_20190714_1422.py index f2a30b8..4e9efb2 100644 --- a/src/newsreader/news/collection/migrations/0004_auto_20190714_1422.py +++ b/src/newsreader/news/collection/migrations/0004_auto_20190714_1422.py @@ -6,6 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0003_auto_20190714_1417")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0005_auto_20200303_1932.py b/src/newsreader/news/collection/migrations/0005_auto_20200303_1932.py index 9d2f615..cdd3e32 100644 --- a/src/newsreader/news/collection/migrations/0005_auto_20200303_1932.py +++ b/src/newsreader/news/collection/migrations/0005_auto_20200303_1932.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0004_auto_20190714_1422")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0006_auto_20200412_1955.py b/src/newsreader/news/collection/migrations/0006_auto_20200412_1955.py index 6745b65..441d7f1 100644 --- a/src/newsreader/news/collection/migrations/0006_auto_20200412_1955.py +++ b/src/newsreader/news/collection/migrations/0006_auto_20200412_1955.py @@ -7,6 +7,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ("collection", "0005_auto_20200303_1932"), diff --git a/src/newsreader/news/collection/migrations/0007_collectionrule_enabled.py b/src/newsreader/news/collection/migrations/0007_collectionrule_enabled.py index a282a42..fe6b0eb 100644 --- a/src/newsreader/news/collection/migrations/0007_collectionrule_enabled.py +++ b/src/newsreader/news/collection/migrations/0007_collectionrule_enabled.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0006_auto_20200412_1955")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0008_collectionrule_type.py b/src/newsreader/news/collection/migrations/0008_collectionrule_type.py index 946296c..bb8975d 100644 --- a/src/newsreader/news/collection/migrations/0008_collectionrule_type.py +++ b/src/newsreader/news/collection/migrations/0008_collectionrule_type.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0007_collectionrule_enabled")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0009_auto_20200807_2030.py b/src/newsreader/news/collection/migrations/0009_auto_20200807_2030.py index 980e0cf..2ce4cb3 100644 --- a/src/newsreader/news/collection/migrations/0009_auto_20200807_2030.py +++ b/src/newsreader/news/collection/migrations/0009_auto_20200807_2030.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0008_collectionrule_type")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0010_auto_20200913_2101.py b/src/newsreader/news/collection/migrations/0010_auto_20200913_2101.py index 68428ca..2f08f6e 100644 --- a/src/newsreader/news/collection/migrations/0010_auto_20200913_2101.py +++ b/src/newsreader/news/collection/migrations/0010_auto_20200913_2101.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0009_auto_20200807_2030")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0011_auto_20200913_2157.py b/src/newsreader/news/collection/migrations/0011_auto_20200913_2157.py index 75d5bf5..308c654 100644 --- a/src/newsreader/news/collection/migrations/0011_auto_20200913_2157.py +++ b/src/newsreader/news/collection/migrations/0011_auto_20200913_2157.py @@ -4,6 +4,7 @@ from django.db import migrations class Migration(migrations.Migration): + dependencies = [("collection", "0010_auto_20200913_2101")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0012_auto_20201219_1331.py b/src/newsreader/news/collection/migrations/0012_auto_20201219_1331.py index 078bb7c..f1b46ef 100644 --- a/src/newsreader/news/collection/migrations/0012_auto_20201219_1331.py +++ b/src/newsreader/news/collection/migrations/0012_auto_20201219_1331.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0011_auto_20200913_2157")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0013_auto_20201219_1345.py b/src/newsreader/news/collection/migrations/0013_auto_20201219_1345.py index 05698ad..93c78a3 100644 --- a/src/newsreader/news/collection/migrations/0013_auto_20201219_1345.py +++ b/src/newsreader/news/collection/migrations/0013_auto_20201219_1345.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0012_auto_20201219_1331")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0014_auto_20201219_1346.py b/src/newsreader/news/collection/migrations/0014_auto_20201219_1346.py index 337f715..57af0c7 100644 --- a/src/newsreader/news/collection/migrations/0014_auto_20201219_1346.py +++ b/src/newsreader/news/collection/migrations/0014_auto_20201219_1346.py @@ -12,6 +12,7 @@ def reset_default_downvotes(apps, schema_editor): class Migration(migrations.Migration): + dependencies = [("collection", "0013_auto_20201219_1345")] operations = [migrations.RunPython(reset_default_downvotes)] diff --git a/src/newsreader/news/collection/migrations/0015_alter_collectionrule_timezone.py b/src/newsreader/news/collection/migrations/0015_alter_collectionrule_timezone.py index 6838bb6..1f29764 100644 --- a/src/newsreader/news/collection/migrations/0015_alter_collectionrule_timezone.py +++ b/src/newsreader/news/collection/migrations/0015_alter_collectionrule_timezone.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("collection", "0014_auto_20201219_1346")] operations = [ diff --git a/src/newsreader/news/collection/migrations/0016_alter_collectionrule_timezone.py b/src/newsreader/news/collection/migrations/0016_alter_collectionrule_timezone.py deleted file mode 100644 index 768d315..0000000 --- a/src/newsreader/news/collection/migrations/0016_alter_collectionrule_timezone.py +++ /dev/null @@ -1,630 +0,0 @@ -# Generated by Django 3.2.25 on 2024-09-06 06:47 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("collection", "0015_alter_collectionrule_timezone"), - ] - - operations = [ - migrations.AlterField( - model_name="collectionrule", - name="timezone", - field=models.CharField( - choices=[ - ("Africa/Abidjan", "Africa/Abidjan"), - ("Africa/Accra", "Africa/Accra"), - ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), - ("Africa/Algiers", "Africa/Algiers"), - ("Africa/Asmara", "Africa/Asmara"), - ("Africa/Asmera", "Africa/Asmera"), - ("Africa/Bamako", "Africa/Bamako"), - ("Africa/Bangui", "Africa/Bangui"), - ("Africa/Banjul", "Africa/Banjul"), - ("Africa/Bissau", "Africa/Bissau"), - ("Africa/Blantyre", "Africa/Blantyre"), - ("Africa/Brazzaville", "Africa/Brazzaville"), - ("Africa/Bujumbura", "Africa/Bujumbura"), - ("Africa/Cairo", "Africa/Cairo"), - ("Africa/Casablanca", "Africa/Casablanca"), - ("Africa/Ceuta", "Africa/Ceuta"), - ("Africa/Conakry", "Africa/Conakry"), - ("Africa/Dakar", "Africa/Dakar"), - ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), - ("Africa/Djibouti", "Africa/Djibouti"), - ("Africa/Douala", "Africa/Douala"), - ("Africa/El_Aaiun", "Africa/El_Aaiun"), - ("Africa/Freetown", "Africa/Freetown"), - ("Africa/Gaborone", "Africa/Gaborone"), - ("Africa/Harare", "Africa/Harare"), - ("Africa/Johannesburg", "Africa/Johannesburg"), - ("Africa/Juba", "Africa/Juba"), - ("Africa/Kampala", "Africa/Kampala"), - ("Africa/Khartoum", "Africa/Khartoum"), - ("Africa/Kigali", "Africa/Kigali"), - ("Africa/Kinshasa", "Africa/Kinshasa"), - ("Africa/Lagos", "Africa/Lagos"), - ("Africa/Libreville", "Africa/Libreville"), - ("Africa/Lome", "Africa/Lome"), - ("Africa/Luanda", "Africa/Luanda"), - ("Africa/Lubumbashi", "Africa/Lubumbashi"), - ("Africa/Lusaka", "Africa/Lusaka"), - ("Africa/Malabo", "Africa/Malabo"), - ("Africa/Maputo", "Africa/Maputo"), - ("Africa/Maseru", "Africa/Maseru"), - ("Africa/Mbabane", "Africa/Mbabane"), - ("Africa/Mogadishu", "Africa/Mogadishu"), - ("Africa/Monrovia", "Africa/Monrovia"), - ("Africa/Nairobi", "Africa/Nairobi"), - ("Africa/Ndjamena", "Africa/Ndjamena"), - ("Africa/Niamey", "Africa/Niamey"), - ("Africa/Nouakchott", "Africa/Nouakchott"), - ("Africa/Ouagadougou", "Africa/Ouagadougou"), - ("Africa/Porto-Novo", "Africa/Porto-Novo"), - ("Africa/Sao_Tome", "Africa/Sao_Tome"), - ("Africa/Timbuktu", "Africa/Timbuktu"), - ("Africa/Tripoli", "Africa/Tripoli"), - ("Africa/Tunis", "Africa/Tunis"), - ("Africa/Windhoek", "Africa/Windhoek"), - ("America/Adak", "America/Adak"), - ("America/Anchorage", "America/Anchorage"), - ("America/Anguilla", "America/Anguilla"), - ("America/Antigua", "America/Antigua"), - ("America/Araguaina", "America/Araguaina"), - ( - "America/Argentina/Buenos_Aires", - "America/Argentina/Buenos_Aires", - ), - ("America/Argentina/Catamarca", "America/Argentina/Catamarca"), - ( - "America/Argentina/ComodRivadavia", - "America/Argentina/ComodRivadavia", - ), - ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), - ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), - ("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"), - ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), - ( - "America/Argentina/Rio_Gallegos", - "America/Argentina/Rio_Gallegos", - ), - ("America/Argentina/Salta", "America/Argentina/Salta"), - ("America/Argentina/San_Juan", "America/Argentina/San_Juan"), - ("America/Argentina/San_Luis", "America/Argentina/San_Luis"), - ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), - ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), - ("America/Aruba", "America/Aruba"), - ("America/Asuncion", "America/Asuncion"), - ("America/Atikokan", "America/Atikokan"), - ("America/Atka", "America/Atka"), - ("America/Bahia", "America/Bahia"), - ("America/Bahia_Banderas", "America/Bahia_Banderas"), - ("America/Barbados", "America/Barbados"), - ("America/Belem", "America/Belem"), - ("America/Belize", "America/Belize"), - ("America/Blanc-Sablon", "America/Blanc-Sablon"), - ("America/Boa_Vista", "America/Boa_Vista"), - ("America/Bogota", "America/Bogota"), - ("America/Boise", "America/Boise"), - ("America/Buenos_Aires", "America/Buenos_Aires"), - ("America/Cambridge_Bay", "America/Cambridge_Bay"), - ("America/Campo_Grande", "America/Campo_Grande"), - ("America/Cancun", "America/Cancun"), - ("America/Caracas", "America/Caracas"), - ("America/Catamarca", "America/Catamarca"), - ("America/Cayenne", "America/Cayenne"), - ("America/Cayman", "America/Cayman"), - ("America/Chicago", "America/Chicago"), - ("America/Chihuahua", "America/Chihuahua"), - ("America/Ciudad_Juarez", "America/Ciudad_Juarez"), - ("America/Coral_Harbour", "America/Coral_Harbour"), - ("America/Cordoba", "America/Cordoba"), - ("America/Costa_Rica", "America/Costa_Rica"), - ("America/Creston", "America/Creston"), - ("America/Cuiaba", "America/Cuiaba"), - ("America/Curacao", "America/Curacao"), - ("America/Danmarkshavn", "America/Danmarkshavn"), - ("America/Dawson", "America/Dawson"), - ("America/Dawson_Creek", "America/Dawson_Creek"), - ("America/Denver", "America/Denver"), - ("America/Detroit", "America/Detroit"), - ("America/Dominica", "America/Dominica"), - ("America/Edmonton", "America/Edmonton"), - ("America/Eirunepe", "America/Eirunepe"), - ("America/El_Salvador", "America/El_Salvador"), - ("America/Ensenada", "America/Ensenada"), - ("America/Fort_Nelson", "America/Fort_Nelson"), - ("America/Fort_Wayne", "America/Fort_Wayne"), - ("America/Fortaleza", "America/Fortaleza"), - ("America/Glace_Bay", "America/Glace_Bay"), - ("America/Godthab", "America/Godthab"), - ("America/Goose_Bay", "America/Goose_Bay"), - ("America/Grand_Turk", "America/Grand_Turk"), - ("America/Grenada", "America/Grenada"), - ("America/Guadeloupe", "America/Guadeloupe"), - ("America/Guatemala", "America/Guatemala"), - ("America/Guayaquil", "America/Guayaquil"), - ("America/Guyana", "America/Guyana"), - ("America/Halifax", "America/Halifax"), - ("America/Havana", "America/Havana"), - ("America/Hermosillo", "America/Hermosillo"), - ("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"), - ("America/Indiana/Knox", "America/Indiana/Knox"), - ("America/Indiana/Marengo", "America/Indiana/Marengo"), - ("America/Indiana/Petersburg", "America/Indiana/Petersburg"), - ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), - ("America/Indiana/Vevay", "America/Indiana/Vevay"), - ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), - ("America/Indiana/Winamac", "America/Indiana/Winamac"), - ("America/Indianapolis", "America/Indianapolis"), - ("America/Inuvik", "America/Inuvik"), - ("America/Iqaluit", "America/Iqaluit"), - ("America/Jamaica", "America/Jamaica"), - ("America/Jujuy", "America/Jujuy"), - ("America/Juneau", "America/Juneau"), - ("America/Kentucky/Louisville", "America/Kentucky/Louisville"), - ("America/Kentucky/Monticello", "America/Kentucky/Monticello"), - ("America/Knox_IN", "America/Knox_IN"), - ("America/Kralendijk", "America/Kralendijk"), - ("America/La_Paz", "America/La_Paz"), - ("America/Lima", "America/Lima"), - ("America/Los_Angeles", "America/Los_Angeles"), - ("America/Louisville", "America/Louisville"), - ("America/Lower_Princes", "America/Lower_Princes"), - ("America/Maceio", "America/Maceio"), - ("America/Managua", "America/Managua"), - ("America/Manaus", "America/Manaus"), - ("America/Marigot", "America/Marigot"), - ("America/Martinique", "America/Martinique"), - ("America/Matamoros", "America/Matamoros"), - ("America/Mazatlan", "America/Mazatlan"), - ("America/Mendoza", "America/Mendoza"), - ("America/Menominee", "America/Menominee"), - ("America/Merida", "America/Merida"), - ("America/Metlakatla", "America/Metlakatla"), - ("America/Mexico_City", "America/Mexico_City"), - ("America/Miquelon", "America/Miquelon"), - ("America/Moncton", "America/Moncton"), - ("America/Monterrey", "America/Monterrey"), - ("America/Montevideo", "America/Montevideo"), - ("America/Montreal", "America/Montreal"), - ("America/Montserrat", "America/Montserrat"), - ("America/Nassau", "America/Nassau"), - ("America/New_York", "America/New_York"), - ("America/Nipigon", "America/Nipigon"), - ("America/Nome", "America/Nome"), - ("America/Noronha", "America/Noronha"), - ("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"), - ("America/North_Dakota/Center", "America/North_Dakota/Center"), - ( - "America/North_Dakota/New_Salem", - "America/North_Dakota/New_Salem", - ), - ("America/Nuuk", "America/Nuuk"), - ("America/Ojinaga", "America/Ojinaga"), - ("America/Panama", "America/Panama"), - ("America/Pangnirtung", "America/Pangnirtung"), - ("America/Paramaribo", "America/Paramaribo"), - ("America/Phoenix", "America/Phoenix"), - ("America/Port-au-Prince", "America/Port-au-Prince"), - ("America/Port_of_Spain", "America/Port_of_Spain"), - ("America/Porto_Acre", "America/Porto_Acre"), - ("America/Porto_Velho", "America/Porto_Velho"), - ("America/Puerto_Rico", "America/Puerto_Rico"), - ("America/Punta_Arenas", "America/Punta_Arenas"), - ("America/Rainy_River", "America/Rainy_River"), - ("America/Rankin_Inlet", "America/Rankin_Inlet"), - ("America/Recife", "America/Recife"), - ("America/Regina", "America/Regina"), - ("America/Resolute", "America/Resolute"), - ("America/Rio_Branco", "America/Rio_Branco"), - ("America/Rosario", "America/Rosario"), - ("America/Santa_Isabel", "America/Santa_Isabel"), - ("America/Santarem", "America/Santarem"), - ("America/Santiago", "America/Santiago"), - ("America/Santo_Domingo", "America/Santo_Domingo"), - ("America/Sao_Paulo", "America/Sao_Paulo"), - ("America/Scoresbysund", "America/Scoresbysund"), - ("America/Shiprock", "America/Shiprock"), - ("America/Sitka", "America/Sitka"), - ("America/St_Barthelemy", "America/St_Barthelemy"), - ("America/St_Johns", "America/St_Johns"), - ("America/St_Kitts", "America/St_Kitts"), - ("America/St_Lucia", "America/St_Lucia"), - ("America/St_Thomas", "America/St_Thomas"), - ("America/St_Vincent", "America/St_Vincent"), - ("America/Swift_Current", "America/Swift_Current"), - ("America/Tegucigalpa", "America/Tegucigalpa"), - ("America/Thule", "America/Thule"), - ("America/Thunder_Bay", "America/Thunder_Bay"), - ("America/Tijuana", "America/Tijuana"), - ("America/Toronto", "America/Toronto"), - ("America/Tortola", "America/Tortola"), - ("America/Vancouver", "America/Vancouver"), - ("America/Virgin", "America/Virgin"), - ("America/Whitehorse", "America/Whitehorse"), - ("America/Winnipeg", "America/Winnipeg"), - ("America/Yakutat", "America/Yakutat"), - ("America/Yellowknife", "America/Yellowknife"), - ("Antarctica/Casey", "Antarctica/Casey"), - ("Antarctica/Davis", "Antarctica/Davis"), - ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), - ("Antarctica/Macquarie", "Antarctica/Macquarie"), - ("Antarctica/Mawson", "Antarctica/Mawson"), - ("Antarctica/McMurdo", "Antarctica/McMurdo"), - ("Antarctica/Palmer", "Antarctica/Palmer"), - ("Antarctica/Rothera", "Antarctica/Rothera"), - ("Antarctica/South_Pole", "Antarctica/South_Pole"), - ("Antarctica/Syowa", "Antarctica/Syowa"), - ("Antarctica/Troll", "Antarctica/Troll"), - ("Antarctica/Vostok", "Antarctica/Vostok"), - ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), - ("Asia/Aden", "Asia/Aden"), - ("Asia/Almaty", "Asia/Almaty"), - ("Asia/Amman", "Asia/Amman"), - ("Asia/Anadyr", "Asia/Anadyr"), - ("Asia/Aqtau", "Asia/Aqtau"), - ("Asia/Aqtobe", "Asia/Aqtobe"), - ("Asia/Ashgabat", "Asia/Ashgabat"), - ("Asia/Ashkhabad", "Asia/Ashkhabad"), - ("Asia/Atyrau", "Asia/Atyrau"), - ("Asia/Baghdad", "Asia/Baghdad"), - ("Asia/Bahrain", "Asia/Bahrain"), - ("Asia/Baku", "Asia/Baku"), - ("Asia/Bangkok", "Asia/Bangkok"), - ("Asia/Barnaul", "Asia/Barnaul"), - ("Asia/Beirut", "Asia/Beirut"), - ("Asia/Bishkek", "Asia/Bishkek"), - ("Asia/Brunei", "Asia/Brunei"), - ("Asia/Calcutta", "Asia/Calcutta"), - ("Asia/Chita", "Asia/Chita"), - ("Asia/Choibalsan", "Asia/Choibalsan"), - ("Asia/Chongqing", "Asia/Chongqing"), - ("Asia/Chungking", "Asia/Chungking"), - ("Asia/Colombo", "Asia/Colombo"), - ("Asia/Dacca", "Asia/Dacca"), - ("Asia/Damascus", "Asia/Damascus"), - ("Asia/Dhaka", "Asia/Dhaka"), - ("Asia/Dili", "Asia/Dili"), - ("Asia/Dubai", "Asia/Dubai"), - ("Asia/Dushanbe", "Asia/Dushanbe"), - ("Asia/Famagusta", "Asia/Famagusta"), - ("Asia/Gaza", "Asia/Gaza"), - ("Asia/Harbin", "Asia/Harbin"), - ("Asia/Hebron", "Asia/Hebron"), - ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), - ("Asia/Hong_Kong", "Asia/Hong_Kong"), - ("Asia/Hovd", "Asia/Hovd"), - ("Asia/Irkutsk", "Asia/Irkutsk"), - ("Asia/Istanbul", "Asia/Istanbul"), - ("Asia/Jakarta", "Asia/Jakarta"), - ("Asia/Jayapura", "Asia/Jayapura"), - ("Asia/Jerusalem", "Asia/Jerusalem"), - ("Asia/Kabul", "Asia/Kabul"), - ("Asia/Kamchatka", "Asia/Kamchatka"), - ("Asia/Karachi", "Asia/Karachi"), - ("Asia/Kashgar", "Asia/Kashgar"), - ("Asia/Kathmandu", "Asia/Kathmandu"), - ("Asia/Katmandu", "Asia/Katmandu"), - ("Asia/Khandyga", "Asia/Khandyga"), - ("Asia/Kolkata", "Asia/Kolkata"), - ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), - ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), - ("Asia/Kuching", "Asia/Kuching"), - ("Asia/Kuwait", "Asia/Kuwait"), - ("Asia/Macao", "Asia/Macao"), - ("Asia/Macau", "Asia/Macau"), - ("Asia/Magadan", "Asia/Magadan"), - ("Asia/Makassar", "Asia/Makassar"), - ("Asia/Manila", "Asia/Manila"), - ("Asia/Muscat", "Asia/Muscat"), - ("Asia/Nicosia", "Asia/Nicosia"), - ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), - ("Asia/Novosibirsk", "Asia/Novosibirsk"), - ("Asia/Omsk", "Asia/Omsk"), - ("Asia/Oral", "Asia/Oral"), - ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), - ("Asia/Pontianak", "Asia/Pontianak"), - ("Asia/Pyongyang", "Asia/Pyongyang"), - ("Asia/Qatar", "Asia/Qatar"), - ("Asia/Qostanay", "Asia/Qostanay"), - ("Asia/Qyzylorda", "Asia/Qyzylorda"), - ("Asia/Rangoon", "Asia/Rangoon"), - ("Asia/Riyadh", "Asia/Riyadh"), - ("Asia/Saigon", "Asia/Saigon"), - ("Asia/Sakhalin", "Asia/Sakhalin"), - ("Asia/Samarkand", "Asia/Samarkand"), - ("Asia/Seoul", "Asia/Seoul"), - ("Asia/Shanghai", "Asia/Shanghai"), - ("Asia/Singapore", "Asia/Singapore"), - ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), - ("Asia/Taipei", "Asia/Taipei"), - ("Asia/Tashkent", "Asia/Tashkent"), - ("Asia/Tbilisi", "Asia/Tbilisi"), - ("Asia/Tehran", "Asia/Tehran"), - ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), - ("Asia/Thimbu", "Asia/Thimbu"), - ("Asia/Thimphu", "Asia/Thimphu"), - ("Asia/Tokyo", "Asia/Tokyo"), - ("Asia/Tomsk", "Asia/Tomsk"), - ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), - ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), - ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), - ("Asia/Urumqi", "Asia/Urumqi"), - ("Asia/Ust-Nera", "Asia/Ust-Nera"), - ("Asia/Vientiane", "Asia/Vientiane"), - ("Asia/Vladivostok", "Asia/Vladivostok"), - ("Asia/Yakutsk", "Asia/Yakutsk"), - ("Asia/Yangon", "Asia/Yangon"), - ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), - ("Asia/Yerevan", "Asia/Yerevan"), - ("Atlantic/Azores", "Atlantic/Azores"), - ("Atlantic/Bermuda", "Atlantic/Bermuda"), - ("Atlantic/Canary", "Atlantic/Canary"), - ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), - ("Atlantic/Faeroe", "Atlantic/Faeroe"), - ("Atlantic/Faroe", "Atlantic/Faroe"), - ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), - ("Atlantic/Madeira", "Atlantic/Madeira"), - ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), - ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), - ("Atlantic/St_Helena", "Atlantic/St_Helena"), - ("Atlantic/Stanley", "Atlantic/Stanley"), - ("Australia/ACT", "Australia/ACT"), - ("Australia/Adelaide", "Australia/Adelaide"), - ("Australia/Brisbane", "Australia/Brisbane"), - ("Australia/Broken_Hill", "Australia/Broken_Hill"), - ("Australia/Canberra", "Australia/Canberra"), - ("Australia/Currie", "Australia/Currie"), - ("Australia/Darwin", "Australia/Darwin"), - ("Australia/Eucla", "Australia/Eucla"), - ("Australia/Hobart", "Australia/Hobart"), - ("Australia/LHI", "Australia/LHI"), - ("Australia/Lindeman", "Australia/Lindeman"), - ("Australia/Lord_Howe", "Australia/Lord_Howe"), - ("Australia/Melbourne", "Australia/Melbourne"), - ("Australia/NSW", "Australia/NSW"), - ("Australia/North", "Australia/North"), - ("Australia/Perth", "Australia/Perth"), - ("Australia/Queensland", "Australia/Queensland"), - ("Australia/South", "Australia/South"), - ("Australia/Sydney", "Australia/Sydney"), - ("Australia/Tasmania", "Australia/Tasmania"), - ("Australia/Victoria", "Australia/Victoria"), - ("Australia/West", "Australia/West"), - ("Australia/Yancowinna", "Australia/Yancowinna"), - ("Brazil/Acre", "Brazil/Acre"), - ("Brazil/DeNoronha", "Brazil/DeNoronha"), - ("Brazil/East", "Brazil/East"), - ("Brazil/West", "Brazil/West"), - ("CET", "CET"), - ("CST6CDT", "CST6CDT"), - ("Canada/Atlantic", "Canada/Atlantic"), - ("Canada/Central", "Canada/Central"), - ("Canada/Eastern", "Canada/Eastern"), - ("Canada/Mountain", "Canada/Mountain"), - ("Canada/Newfoundland", "Canada/Newfoundland"), - ("Canada/Pacific", "Canada/Pacific"), - ("Canada/Saskatchewan", "Canada/Saskatchewan"), - ("Canada/Yukon", "Canada/Yukon"), - ("Chile/Continental", "Chile/Continental"), - ("Chile/EasterIsland", "Chile/EasterIsland"), - ("Cuba", "Cuba"), - ("EET", "EET"), - ("EST", "EST"), - ("EST5EDT", "EST5EDT"), - ("Egypt", "Egypt"), - ("Eire", "Eire"), - ("Etc/GMT", "Etc/GMT"), - ("Etc/GMT+0", "Etc/GMT+0"), - ("Etc/GMT+1", "Etc/GMT+1"), - ("Etc/GMT+10", "Etc/GMT+10"), - ("Etc/GMT+11", "Etc/GMT+11"), - ("Etc/GMT+12", "Etc/GMT+12"), - ("Etc/GMT+2", "Etc/GMT+2"), - ("Etc/GMT+3", "Etc/GMT+3"), - ("Etc/GMT+4", "Etc/GMT+4"), - ("Etc/GMT+5", "Etc/GMT+5"), - ("Etc/GMT+6", "Etc/GMT+6"), - ("Etc/GMT+7", "Etc/GMT+7"), - ("Etc/GMT+8", "Etc/GMT+8"), - ("Etc/GMT+9", "Etc/GMT+9"), - ("Etc/GMT-0", "Etc/GMT-0"), - ("Etc/GMT-1", "Etc/GMT-1"), - ("Etc/GMT-10", "Etc/GMT-10"), - ("Etc/GMT-11", "Etc/GMT-11"), - ("Etc/GMT-12", "Etc/GMT-12"), - ("Etc/GMT-13", "Etc/GMT-13"), - ("Etc/GMT-14", "Etc/GMT-14"), - ("Etc/GMT-2", "Etc/GMT-2"), - ("Etc/GMT-3", "Etc/GMT-3"), - ("Etc/GMT-4", "Etc/GMT-4"), - ("Etc/GMT-5", "Etc/GMT-5"), - ("Etc/GMT-6", "Etc/GMT-6"), - ("Etc/GMT-7", "Etc/GMT-7"), - ("Etc/GMT-8", "Etc/GMT-8"), - ("Etc/GMT-9", "Etc/GMT-9"), - ("Etc/GMT0", "Etc/GMT0"), - ("Etc/Greenwich", "Etc/Greenwich"), - ("Etc/UCT", "Etc/UCT"), - ("Etc/UTC", "Etc/UTC"), - ("Etc/Universal", "Etc/Universal"), - ("Etc/Zulu", "Etc/Zulu"), - ("Europe/Amsterdam", "Europe/Amsterdam"), - ("Europe/Andorra", "Europe/Andorra"), - ("Europe/Astrakhan", "Europe/Astrakhan"), - ("Europe/Athens", "Europe/Athens"), - ("Europe/Belfast", "Europe/Belfast"), - ("Europe/Belgrade", "Europe/Belgrade"), - ("Europe/Berlin", "Europe/Berlin"), - ("Europe/Bratislava", "Europe/Bratislava"), - ("Europe/Brussels", "Europe/Brussels"), - ("Europe/Bucharest", "Europe/Bucharest"), - ("Europe/Budapest", "Europe/Budapest"), - ("Europe/Busingen", "Europe/Busingen"), - ("Europe/Chisinau", "Europe/Chisinau"), - ("Europe/Copenhagen", "Europe/Copenhagen"), - ("Europe/Dublin", "Europe/Dublin"), - ("Europe/Gibraltar", "Europe/Gibraltar"), - ("Europe/Guernsey", "Europe/Guernsey"), - ("Europe/Helsinki", "Europe/Helsinki"), - ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), - ("Europe/Istanbul", "Europe/Istanbul"), - ("Europe/Jersey", "Europe/Jersey"), - ("Europe/Kaliningrad", "Europe/Kaliningrad"), - ("Europe/Kiev", "Europe/Kiev"), - ("Europe/Kirov", "Europe/Kirov"), - ("Europe/Kyiv", "Europe/Kyiv"), - ("Europe/Lisbon", "Europe/Lisbon"), - ("Europe/Ljubljana", "Europe/Ljubljana"), - ("Europe/London", "Europe/London"), - ("Europe/Luxembourg", "Europe/Luxembourg"), - ("Europe/Madrid", "Europe/Madrid"), - ("Europe/Malta", "Europe/Malta"), - ("Europe/Mariehamn", "Europe/Mariehamn"), - ("Europe/Minsk", "Europe/Minsk"), - ("Europe/Monaco", "Europe/Monaco"), - ("Europe/Moscow", "Europe/Moscow"), - ("Europe/Nicosia", "Europe/Nicosia"), - ("Europe/Oslo", "Europe/Oslo"), - ("Europe/Paris", "Europe/Paris"), - ("Europe/Podgorica", "Europe/Podgorica"), - ("Europe/Prague", "Europe/Prague"), - ("Europe/Riga", "Europe/Riga"), - ("Europe/Rome", "Europe/Rome"), - ("Europe/Samara", "Europe/Samara"), - ("Europe/San_Marino", "Europe/San_Marino"), - ("Europe/Sarajevo", "Europe/Sarajevo"), - ("Europe/Saratov", "Europe/Saratov"), - ("Europe/Simferopol", "Europe/Simferopol"), - ("Europe/Skopje", "Europe/Skopje"), - ("Europe/Sofia", "Europe/Sofia"), - ("Europe/Stockholm", "Europe/Stockholm"), - ("Europe/Tallinn", "Europe/Tallinn"), - ("Europe/Tirane", "Europe/Tirane"), - ("Europe/Tiraspol", "Europe/Tiraspol"), - ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), - ("Europe/Uzhgorod", "Europe/Uzhgorod"), - ("Europe/Vaduz", "Europe/Vaduz"), - ("Europe/Vatican", "Europe/Vatican"), - ("Europe/Vienna", "Europe/Vienna"), - ("Europe/Vilnius", "Europe/Vilnius"), - ("Europe/Volgograd", "Europe/Volgograd"), - ("Europe/Warsaw", "Europe/Warsaw"), - ("Europe/Zagreb", "Europe/Zagreb"), - ("Europe/Zaporozhye", "Europe/Zaporozhye"), - ("Europe/Zurich", "Europe/Zurich"), - ("GB", "GB"), - ("GB-Eire", "GB-Eire"), - ("GMT", "GMT"), - ("GMT+0", "GMT+0"), - ("GMT-0", "GMT-0"), - ("GMT0", "GMT0"), - ("Greenwich", "Greenwich"), - ("HST", "HST"), - ("Hongkong", "Hongkong"), - ("Iceland", "Iceland"), - ("Indian/Antananarivo", "Indian/Antananarivo"), - ("Indian/Chagos", "Indian/Chagos"), - ("Indian/Christmas", "Indian/Christmas"), - ("Indian/Cocos", "Indian/Cocos"), - ("Indian/Comoro", "Indian/Comoro"), - ("Indian/Kerguelen", "Indian/Kerguelen"), - ("Indian/Mahe", "Indian/Mahe"), - ("Indian/Maldives", "Indian/Maldives"), - ("Indian/Mauritius", "Indian/Mauritius"), - ("Indian/Mayotte", "Indian/Mayotte"), - ("Indian/Reunion", "Indian/Reunion"), - ("Iran", "Iran"), - ("Israel", "Israel"), - ("Jamaica", "Jamaica"), - ("Japan", "Japan"), - ("Kwajalein", "Kwajalein"), - ("Libya", "Libya"), - ("MET", "MET"), - ("MST", "MST"), - ("MST7MDT", "MST7MDT"), - ("Mexico/BajaNorte", "Mexico/BajaNorte"), - ("Mexico/BajaSur", "Mexico/BajaSur"), - ("Mexico/General", "Mexico/General"), - ("NZ", "NZ"), - ("NZ-CHAT", "NZ-CHAT"), - ("Navajo", "Navajo"), - ("PRC", "PRC"), - ("PST8PDT", "PST8PDT"), - ("Pacific/Apia", "Pacific/Apia"), - ("Pacific/Auckland", "Pacific/Auckland"), - ("Pacific/Bougainville", "Pacific/Bougainville"), - ("Pacific/Chatham", "Pacific/Chatham"), - ("Pacific/Chuuk", "Pacific/Chuuk"), - ("Pacific/Easter", "Pacific/Easter"), - ("Pacific/Efate", "Pacific/Efate"), - ("Pacific/Enderbury", "Pacific/Enderbury"), - ("Pacific/Fakaofo", "Pacific/Fakaofo"), - ("Pacific/Fiji", "Pacific/Fiji"), - ("Pacific/Funafuti", "Pacific/Funafuti"), - ("Pacific/Galapagos", "Pacific/Galapagos"), - ("Pacific/Gambier", "Pacific/Gambier"), - ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), - ("Pacific/Guam", "Pacific/Guam"), - ("Pacific/Honolulu", "Pacific/Honolulu"), - ("Pacific/Johnston", "Pacific/Johnston"), - ("Pacific/Kanton", "Pacific/Kanton"), - ("Pacific/Kiritimati", "Pacific/Kiritimati"), - ("Pacific/Kosrae", "Pacific/Kosrae"), - ("Pacific/Kwajalein", "Pacific/Kwajalein"), - ("Pacific/Majuro", "Pacific/Majuro"), - ("Pacific/Marquesas", "Pacific/Marquesas"), - ("Pacific/Midway", "Pacific/Midway"), - ("Pacific/Nauru", "Pacific/Nauru"), - ("Pacific/Niue", "Pacific/Niue"), - ("Pacific/Norfolk", "Pacific/Norfolk"), - ("Pacific/Noumea", "Pacific/Noumea"), - ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), - ("Pacific/Palau", "Pacific/Palau"), - ("Pacific/Pitcairn", "Pacific/Pitcairn"), - ("Pacific/Pohnpei", "Pacific/Pohnpei"), - ("Pacific/Ponape", "Pacific/Ponape"), - ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), - ("Pacific/Rarotonga", "Pacific/Rarotonga"), - ("Pacific/Saipan", "Pacific/Saipan"), - ("Pacific/Samoa", "Pacific/Samoa"), - ("Pacific/Tahiti", "Pacific/Tahiti"), - ("Pacific/Tarawa", "Pacific/Tarawa"), - ("Pacific/Tongatapu", "Pacific/Tongatapu"), - ("Pacific/Truk", "Pacific/Truk"), - ("Pacific/Wake", "Pacific/Wake"), - ("Pacific/Wallis", "Pacific/Wallis"), - ("Pacific/Yap", "Pacific/Yap"), - ("Poland", "Poland"), - ("Portugal", "Portugal"), - ("ROC", "ROC"), - ("ROK", "ROK"), - ("Singapore", "Singapore"), - ("Turkey", "Turkey"), - ("UCT", "UCT"), - ("US/Alaska", "US/Alaska"), - ("US/Aleutian", "US/Aleutian"), - ("US/Arizona", "US/Arizona"), - ("US/Central", "US/Central"), - ("US/East-Indiana", "US/East-Indiana"), - ("US/Eastern", "US/Eastern"), - ("US/Hawaii", "US/Hawaii"), - ("US/Indiana-Starke", "US/Indiana-Starke"), - ("US/Michigan", "US/Michigan"), - ("US/Mountain", "US/Mountain"), - ("US/Pacific", "US/Pacific"), - ("US/Samoa", "US/Samoa"), - ("UTC", "UTC"), - ("Universal", "Universal"), - ("W-SU", "W-SU"), - ("WET", "WET"), - ("Zulu", "Zulu"), - ], - default="UTC", - max_length=100, - ), - ), - ] diff --git a/src/newsreader/news/collection/migrations/0017_remove_collectionrule_timezone.py b/src/newsreader/news/collection/migrations/0017_remove_collectionrule_timezone.py deleted file mode 100644 index db72ecc..0000000 --- a/src/newsreader/news/collection/migrations/0017_remove_collectionrule_timezone.py +++ /dev/null @@ -1,16 +0,0 @@ -# Generated by Django 4.2.16 on 2024-09-07 17:07 - -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("collection", "0016_alter_collectionrule_timezone"), - ] - - operations = [ - migrations.RemoveField( - model_name="collectionrule", - name="timezone", - ), - ] diff --git a/src/newsreader/news/collection/migrations/0018_remove_collectionrule_reddit_allow_nfsw_and_more.py b/src/newsreader/news/collection/migrations/0018_remove_collectionrule_reddit_allow_nfsw_and_more.py deleted file mode 100644 index cc61aee..0000000 --- a/src/newsreader/news/collection/migrations/0018_remove_collectionrule_reddit_allow_nfsw_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 4.2.16 on 2025-03-26 08:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("collection", "0017_remove_collectionrule_timezone"), - ] - - operations = [ - migrations.RemoveField( - model_name="collectionrule", - name="reddit_allow_nfsw", - ), - migrations.RemoveField( - model_name="collectionrule", - name="reddit_allow_spoiler", - ), - migrations.RemoveField( - model_name="collectionrule", - name="reddit_allow_viewed", - ), - migrations.RemoveField( - model_name="collectionrule", - name="reddit_comments_min", - ), - migrations.RemoveField( - model_name="collectionrule", - name="reddit_downvotes_max", - ), - migrations.RemoveField( - model_name="collectionrule", - name="reddit_upvotes_min", - ), - migrations.RemoveField( - model_name="collectionrule", - name="screen_name", - ), - migrations.AlterField( - model_name="collectionrule", - name="type", - field=models.CharField( - choices=[("feed", "Feed")], default="feed", max_length=20 - ), - ), - ] diff --git a/src/newsreader/news/collection/models.py b/src/newsreader/news/collection/models.py index e665b16..907de11 100644 --- a/src/newsreader/news/collection/models.py +++ b/src/newsreader/news/collection/models.py @@ -2,6 +2,8 @@ from django.db import models from django.urls import reverse from django.utils.translation import gettext as _ +import pytz + from newsreader.core.models import TimeStampedModel from newsreader.news.collection.choices import RuleTypeChoices @@ -23,6 +25,12 @@ class CollectionRule(TimeStampedModel): ) favicon = models.URLField(blank=True, null=True) + timezone = models.CharField( + choices=((timezone, timezone) for timezone in pytz.all_timezones), + max_length=100, + default=str(pytz.utc), + ) + category = models.ForeignKey( "core.Category", blank=True, @@ -48,6 +56,25 @@ class CollectionRule(TimeStampedModel): on_delete=models.CASCADE, ) + # Reddit + reddit_allow_nfsw = models.BooleanField(_("Allow NSFW posts"), default=False) + reddit_allow_spoiler = models.BooleanField(_("Allow spoilers"), default=False) + reddit_allow_viewed = models.BooleanField( + _("Allow already seen posts"), default=True + ) + reddit_upvotes_min = models.PositiveIntegerField( + _("Minimum amount of upvotes"), default=0 + ) + reddit_downvotes_max = models.PositiveIntegerField( + _("Maximum amount of downvotes"), blank=True, null=True + ) + reddit_comments_min = models.PositiveIntegerField( + _("Minimum amount of comments"), default=0 + ) + + # Twitter + screen_name = models.CharField(max_length=255, blank=True, null=True) + objects = CollectionRuleQuerySet.as_manager() def __str__(self): @@ -55,10 +82,26 @@ class CollectionRule(TimeStampedModel): @property def update_url(self): + if self.type == RuleTypeChoices.subreddit: + return reverse("news:collection:subreddit-update", kwargs={"pk": self.pk}) + elif self.type == RuleTypeChoices.twitter_timeline: + return reverse( + "news:collection:twitter-timeline-update", kwargs={"pk": self.pk} + ) + return reverse("news:collection:feed-update", kwargs={"pk": self.pk}) @property def source_url(self): + if self.type == RuleTypeChoices.subreddit: + from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL + + return self.url.replace(REDDIT_API_URL, REDDIT_URL) + elif self.type == RuleTypeChoices.twitter_timeline: + from newsreader.news.collection.twitter import TWITTER_URL + + return f"{TWITTER_URL}/{self.screen_name}" + return self.url @property diff --git a/src/newsreader/news/collection/reddit.py b/src/newsreader/news/collection/reddit.py new file mode 100644 index 0000000..cc1608a --- /dev/null +++ b/src/newsreader/news/collection/reddit.py @@ -0,0 +1,427 @@ +import logging + +from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import datetime, timedelta +from html import unescape +from json.decoder import JSONDecodeError +from urllib.parse import urlencode +from uuid import uuid4 + +from django.conf import settings +from django.core.cache import cache +from django.utils import timezone +from django.utils.html import format_html + +import pytz +import requests + +from newsreader.news.collection.base import ( + PostBuilder, + PostClient, + PostCollector, + PostStream, + Scheduler, +) +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.constants import ( + WHITELISTED_ATTRIBUTES, + WHITELISTED_TAGS, +) +from newsreader.news.collection.exceptions import ( + BuilderDuplicateException, + BuilderException, + BuilderMissingDataException, + BuilderParseException, + BuilderSkippedException, + StreamDeniedException, + StreamException, + StreamParseException, + StreamTooManyException, +) +from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.tasks import RedditTokenTask +from newsreader.news.collection.utils import fetch, post, truncate_text +from newsreader.news.core.models import Post + + +logger = logging.getLogger(__name__) + + +REDDIT_URL = "https://www.reddit.com" +REDDIT_API_URL = "https://oauth.reddit.com" + +RATE_LIMIT = 60 +RATE_LIMIT_DURATION = timedelta(seconds=60) + +REDDIT_IMAGE_EXTENSIONS = (".jpg", ".png", ".gif") +REDDIT_VIDEO_EXTENSIONS = (".mp4", ".gifv", ".webm") + +# see type prefixes on https://www.reddit.com/dev/api/ +REDDIT_POST = "t3" + + +def get_reddit_authorization_url(user): + state = str(uuid4()) + cache.set(f"{user.email}-reddit-auth", state) + + params = { + "client_id": settings.REDDIT_CLIENT_ID, + "redirect_uri": settings.REDDIT_REDIRECT_URL, + "state": state, + "response_type": "code", + "duration": "permanent", + "scope": "identity,mysubreddits,save,read", + } + + authorization_url = f"{REDDIT_URL}/api/v1/authorize" + return f"{authorization_url}?{urlencode(params)}" + + +def get_reddit_access_token(code, user): + client_auth = requests.auth.HTTPBasicAuth( + settings.REDDIT_CLIENT_ID, settings.REDDIT_CLIENT_SECRET + ) + + response = post( + f"{REDDIT_URL}/api/v1/access_token", + data={ + "redirect_uri": settings.REDDIT_REDIRECT_URL, + "grant_type": "authorization_code", + "code": code, + }, + auth=client_auth, + ) + + response_data = response.json() + + user.reddit_access_token = response_data["access_token"] + user.reddit_refresh_token = response_data["refresh_token"] + user.save() + + cache.delete(f"{user.email}-reddit-auth") + + return response_data["access_token"], response_data["refresh_token"] + + +# Note that the API always returns 204's with correct basic auth headers +def revoke_reddit_token(user): + client_auth = requests.auth.HTTPBasicAuth( + settings.REDDIT_CLIENT_ID, settings.REDDIT_CLIENT_SECRET + ) + + response = post( + f"{REDDIT_URL}/api/v1/revoke_token", + data={"token": user.reddit_refresh_token, "token_type_hint": "refresh_token"}, + auth=client_auth, + ) + + return response.status_code == 204 + + +class RedditBuilder(PostBuilder): + rule_type = RuleTypeChoices.subreddit + + def build(self): + results = {} + + if not "data" in self.payload or not "children" in self.payload["data"]: + return + + entries = self.payload["data"]["children"] + + for entry in entries: + try: + post = self.build_post(entry) + except BuilderDuplicateException: + logger.warning("Skipping duplicate post") + continue + except BuilderSkippedException as e: + logger.warning(e.message) + continue + except BuilderException: + logger.exception("Failed building post") + continue + + identifier = post.remote_identifier + results[identifier] = post + + self.instances = results.values() + + def build_post(self, entry): + rule = self.stream.rule + entry_data = entry.get("data", {}) + remote_identifier = entry_data.get("id", "") + kind = entry.get("kind") + + if remote_identifier in self.existing_posts: + raise BuilderDuplicateException(payload=entry) + elif kind != REDDIT_POST: + raise BuilderParseException( + message=f"Payload is not an reddit post, its of kind {kind}", + payload=entry, + ) + elif not entry_data: + raise BuilderMissingDataException( + message=f"Post {remote_identifier} did not contain any data", + payload=entry, + ) + + try: + title = entry_data["title"] + author = entry_data["author"] + + post_url_fragment = entry_data["permalink"] + direct_url = entry_data["url"] + + is_text = entry_data["is_self"] + is_video = entry_data["is_video"] + + is_nsfw = entry_data["over_18"] + is_spoiler = entry_data["spoiler"] + is_viewed = entry_data["clicked"] + upvotes = entry_data["ups"] + downvotes = entry_data["downs"] + comments = entry_data["num_comments"] + except KeyError as e: + raise BuilderMissingDataException(payload=entry) from e + + if not rule.reddit_allow_nfsw and is_nsfw: + raise BuilderSkippedException("Rule does not allow NSFW posts") + elif not rule.reddit_allow_spoiler and is_spoiler: + raise BuilderSkippedException("Rule does not allow spoilers") + elif not rule.reddit_allow_viewed and is_viewed: + raise BuilderSkippedException("Post was already seen by user") + elif not upvotes >= rule.reddit_upvotes_min: + raise BuilderSkippedException( + "Post does not meet minimum amount of upvotes" + ) + elif ( + rule.reddit_downvotes_max is not None + and downvotes > rule.reddit_downvotes_max + ): + raise BuilderSkippedException("Post has more downvotes than allowed") + elif not comments >= rule.reddit_comments_min: + raise BuilderSkippedException("Post does not have enough comments") + + title = truncate_text(Post, "title", title) + author = truncate_text(Post, "author", author) + + if is_text: + body = self.get_text_post(entry_data) + elif direct_url.endswith(REDDIT_IMAGE_EXTENSIONS): + body = self.get_image_post(title, direct_url) + elif is_video: + body = self.get_native_video_post(entry_data) + elif direct_url.endswith(REDDIT_VIDEO_EXTENSIONS): + body = self.get_video_post(direct_url) + else: + body = self.get_url_post(title, direct_url) + + try: + parsed_date = datetime.fromtimestamp(entry_data["created_utc"]) + created_date = pytz.utc.localize(parsed_date) + except (OverflowError, OSError) as e: + raise BuilderParseException(payload=entry) from e + except KeyError as e: + raise BuilderMissingDataException(payload=entry) from e + + post_entry = { + "remote_identifier": remote_identifier, + "title": title, + "body": body, + "author": author, + "url": f"{REDDIT_URL}{post_url_fragment}", + "publication_date": created_date, + "rule": rule, + } + + return Post(**post_entry) + + def get_text_post(self, entry): + try: + uncleaned_body = entry["selftext_html"] + except KeyError as e: + raise BuilderMissingDataException(payload=entry) from e + + unescaped_body = unescape(uncleaned_body) if uncleaned_body else "" + return self.sanitize_fragment(unescaped_body) if unescaped_body else "" + + def get_image_post(self, title, url): + return format_html( + "
    {title}
    ", + url=url, + title=title, + ) + + def get_native_video_post(self, entry): + try: + video_info = entry["secure_media"]["reddit_video"] + except KeyError as e: + raise BuilderMissingDataException(payload=entry) from e + + return format_html( + "
    ", + url=video_info["fallback_url"], + ) + + def get_video_post(self, url): + extension = next( + extension.replace(".", "") + for extension in REDDIT_VIDEO_EXTENSIONS + if url.endswith(extension) + ) + + if extension == "gifv": + return format_html( + "
    ", + url=url.replace(extension, "mp4"), + ) + + return format_html( + "
    ", + url=url, + extension=extension, + ) + + def get_url_post(self, title, url): + return format_html( + "", + url=url, + title=title, + ) + + def __str__(self): + return f"{self.stream.rule.pk}: RedditBuilder" + + +class RedditStream(PostStream): + rule_type = RuleTypeChoices.subreddit + headers = {} + + def __init__(self, rule): + super().__init__(rule) + + self.headers = { + f"Authorization": f"bearer {self.rule.user.reddit_access_token}" + } + + def read(self): + response = fetch(self.rule.url, headers=self.headers) + + return self.parse(response), self + + def parse(self, response): + try: + return response.json() + except JSONDecodeError as e: + raise StreamParseException( + response=response, message="Failed parsing json" + ) from e + + def __str__(self): + return f"{self.rule.pk}: RedditStream" + + +class RedditClient(PostClient): + stream = RedditStream + + def __enter__(self): + streams = [[self.stream(rule) for rule in batch] for batch in self.rules] + rate_limitted = False + + with ThreadPoolExecutor(max_workers=10) as executor: + for batch in streams: + futures = {executor.submit(stream.read): stream for stream in batch} + + if rate_limitted: + logger.warning("Aborting requests, ratelimit hit") + break + + for future in as_completed(futures): + stream = futures[future] + + try: + response_data = future.result() + + stream.rule.error = None + stream.rule.succeeded = True + + yield response_data + except StreamDeniedException as e: + logger.warning( + f"Access token expired for user {stream.rule.user.pk}" + ) + + stream.rule.user.reddit_access_token = None + stream.rule.user.save() + + self.set_rule_error(stream.rule, e) + + RedditTokenTask.delay(stream.rule.user.pk) + + break + except StreamTooManyException as e: + logger.exception("Ratelimit hit, aborting batched subreddits") + + self.set_rule_error(stream.rule, e) + + rate_limitted = True + break + except StreamException as e: + logger.exception( + f"Stream failed reading content from {stream.rule.url}" + ) + + self.set_rule_error(stream.rule, e) + + continue + finally: + stream.rule.last_run = timezone.now() + stream.rule.save() + + +class RedditCollector(PostCollector): + builder = RedditBuilder + client = RedditClient + + +class RedditScheduler(Scheduler): + max_amount = RATE_LIMIT + max_user_amount = RATE_LIMIT / 4 + + def __init__(self, subreddits=[]): + if not subreddits: + self.subreddits = CollectionRule.objects.filter( + type=RuleTypeChoices.subreddit, + user__reddit_access_token__isnull=False, + user__reddit_refresh_token__isnull=False, + enabled=True, + ).order_by("last_run")[:200] + else: + self.subreddits = subreddits + + def get_scheduled_rules(self): + rule_mapping = {} + current_amount = 0 + + for subreddit in self.subreddits: + user_pk = subreddit.user.pk + + if current_amount == self.max_amount: + break + + if user_pk in rule_mapping: + max_amount_reached = len(rule_mapping[user_pk]) == self.max_user_amount + + if max_amount_reached: + continue + + rule_mapping[user_pk].append(subreddit) + current_amount += 1 + + continue + + rule_mapping[user_pk] = [subreddit] + current_amount += 1 + + return list(rule_mapping.values()) diff --git a/src/newsreader/news/collection/tasks.py b/src/newsreader/news/collection/tasks.py index b61936a..799a101 100644 --- a/src/newsreader/news/collection/tasks.py +++ b/src/newsreader/news/collection/tasks.py @@ -1,4 +1,9 @@ +from django.conf import settings from django.core.exceptions import ObjectDoesNotExist +from django.core.mail import send_mail +from django.utils.translation import ugettext as _ + +import requests from celery.exceptions import Reject from celery.utils.log import get_task_logger @@ -7,6 +12,7 @@ from newsreader.accounts.models import User from newsreader.celery import app from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.feed import FeedCollector +from newsreader.news.collection.utils import post from newsreader.utils.celery import MemCacheLock @@ -42,6 +48,117 @@ class FeedTask(app.Task): raise Reject(reason="Task already running", requeue=False) +class RedditTask(app.Task): + name = "RedditTask" + ignore_result = True + + def run(self): + from newsreader.news.collection.reddit import RedditCollector, RedditScheduler + + with MemCacheLock("reddit-task", self.app.oid) as acquired: + if acquired: + logger.info(f"Running reddit task") + + scheduler = RedditScheduler() + subreddits = scheduler.get_scheduled_rules() + + collector = RedditCollector() + collector.collect(rules=subreddits) + else: + logger.warning(f"Cancelling task due to existing lock") + + raise Reject(reason="Task already running", requeue=False) + + +class RedditTokenTask(app.Task): + name = "RedditTokenTask" + ignore_result = True + + def run(self, user_pk): + from newsreader.news.collection.reddit import REDDIT_URL + + try: + user = User.objects.get(pk=user_pk) + except ObjectDoesNotExist: + message = f"User {user_pk} does not exist" + logger.exception(message) + + raise Reject(reason=message, requeue=False) + + if not user.reddit_refresh_token: + raise Reject(reason=f"User {user_pk} has no refresh token", requeue=False) + + client_auth = requests.auth.HTTPBasicAuth( + settings.REDDIT_CLIENT_ID, settings.REDDIT_CLIENT_SECRET + ) + + try: + response = post( + f"{REDDIT_URL}/api/v1/access_token", + data={ + "grant_type": "refresh_token", + "refresh_token": user.reddit_refresh_token, + }, + auth=client_auth, + ) + except StreamException: + logger.exception( + f"Failed refreshing reddit access token for user {user_pk}" + ) + + user.reddit_refresh_token = None + user.save() + + message = _( + "Your Reddit account credentials have expired. Re-authenticate in" + " the settings page to keep retrieving Reddit specific information" + " from your account." + ) + + send_mail( + "Reddit account needs re-authentication", message, None, [user.email] + ) + return + + response_data = response.json() + + user.reddit_access_token = response_data["access_token"] + user.save() + + +class TwitterTimelineTask(app.Task): + name = "TwitterTimelineTask" + ignore_result = True + + def run(self, user_pk): + from newsreader.news.collection.twitter import ( + TwitterCollector, + TwitterTimeLineScheduler, + ) + + try: + user = User.objects.get(pk=user_pk) + except ObjectDoesNotExist: + message = f"User {user_pk} does not exist" + logger.exception(message) + + raise Reject(reason=message, requeue=False) + + with MemCacheLock("f{user.email}-timeline-task", self.app.oid) as acquired: + if acquired: + logger.info(f"Running twitter timeline task for user {user_pk}") + + scheduler = TwitterTimeLineScheduler(user) + timelines = scheduler.get_scheduled_rules() + + collector = TwitterCollector() + collector.collect(rules=timelines) + else: + logger.warning(f"Cancelling task due to existing lock") + + raise Reject(reason="Task already running", requeue=False) + + class FaviconTask(app.Task): name = "FaviconTask" ignore_result = True @@ -65,11 +182,26 @@ class FaviconTask(app.Task): collector = FaviconCollector() collector.collect(rules=rules) + + third_party_rules = user.rules.enabled().exclude( + type=RuleTypeChoices.feed + ) + + for rule in third_party_rules: + if rule.type == RuleTypeChoices.subreddit: + rule.favicon = "https://www.reddit.com/favicon.ico" + rule.save() + elif rule.type == RuleTypeChoices.twitter_timeline: + rule.favicon = "https://abs.twimg.com/favicons/favicon.ico" + rule.save() else: - logger.warning("Cancelling task due to existing lock") + logger.warning(f"Cancelling task due to existing lock") raise Reject(reason="Task already running", requeue=False) FeedTask = app.register_task(FeedTask()) FaviconTask = app.register_task(FaviconTask()) +RedditTask = app.register_task(RedditTask()) +RedditTokenTask = app.register_task(RedditTokenTask()) +TwitterTimelineTask = app.register_task(TwitterTimelineTask()) diff --git a/src/newsreader/news/collection/templates/news/collection/views/feed-create.html b/src/newsreader/news/collection/templates/news/collection/views/feed-create.html index ed5aef8..c24791a 100644 --- a/src/newsreader/news/collection/templates/news/collection/views/feed-create.html +++ b/src/newsreader/news/collection/templates/news/collection/views/feed-create.html @@ -1,13 +1,9 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load static %} - {% block content %} - {% url "news:collection:rules" as cancel_url %} - -
    -
    - {% include "components/form/form.html" with form=form title="Add a feed" cancel_url=cancel_url confirm_text="Add feed" %} -
    +
    + {% url "news:collection:rules" as cancel_url %} + {% include "components/form/form.html" with form=form title="Add a feed" cancel_url=cancel_url confirm_text="Add feed" %}
    {% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/feed-update.html b/src/newsreader/news/collection/templates/news/collection/views/feed-update.html index ea614ef..33b1faf 100644 --- a/src/newsreader/news/collection/templates/news/collection/views/feed-update.html +++ b/src/newsreader/news/collection/templates/news/collection/views/feed-update.html @@ -1,17 +1,14 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load static i18n %} {% block content %} - {% url "news:collection:rules" as cancel_url %} - {% trans "Failed to retrieve posts" as title %} +
    + {% if feed.error %} + {% trans "Failed to retrieve posts" as title %} + {% include "components/textbox/textbox.html" with title=title body=feed.error class="text-section--error" only %} + {% endif %} -
    -
    - {% if feed.error %} - {% include "components/textbox/textbox.html" with title=title body=feed.error class="text-section--error" only %} - {% endif %} - - {% include "components/form/form.html" with form=form title="Update feed" cancel_url=cancel_url confirm_text="Save feed" %} -
    + {% url "news:collection:rules" as cancel_url %} + {% include "components/form/form.html" with form=form title="Update feed" cancel_url=cancel_url confirm_text="Save feed" only %}
    {% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/import.html b/src/newsreader/news/collection/templates/news/collection/views/import.html index b93894c..9719847 100644 --- a/src/newsreader/news/collection/templates/news/collection/views/import.html +++ b/src/newsreader/news/collection/templates/news/collection/views/import.html @@ -1,13 +1,9 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load static %} - {% block content %} - {% url "news:collection:rules" as cancel_url %} - -
    -
    - {% include "components/form/form.html" with form=form title="Import an OPML file" cancel_url=cancel_url confirm_text="Import feeds" %} -
    +
    + {% url "news:collection:rules" as cancel_url %} + {% include "components/form/form.html" with form=form title="Import an OPML file" cancel_url=cancel_url confirm_text="Import feeds" %}
    {% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/rules.html b/src/newsreader/news/collection/templates/news/collection/views/rules.html index 77c2034..ea6aaf8 100644 --- a/src/newsreader/news/collection/templates/news/collection/views/rules.html +++ b/src/newsreader/news/collection/templates/news/collection/views/rules.html @@ -1,29 +1,25 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load i18n static filters %} {% block content %} -
    -
    +
    {% csrf_token %}
    - - - + + +
    @@ -34,69 +30,47 @@ {% include "components/form/checkbox.html" with id="select-all" data_input="rules" id_for_label="select-all" %} - - {% trans "Name" %} - - - {% trans "Category" %} - - - {% trans "URL" %} - - - {% trans "Successfuly ran" %} - - - {% trans "Enabled" %} - + {% trans "Name" %} + {% trans "Category" %} + {% trans "URL" %} + {% trans "Successfuly ran" %} + {% trans "Enabled" %} - + {% for rule in rules %} - - - {% with rule|id_for_label:"rules" as id_for_label %} - {% include "components/form/checkbox.html" with name="rules" value=rule.pk id=id_for_label - id_for_label=id_for_label %} - {% endwith %} - - - - - {{ rule.name }} - - - - - {% if rule.category %} - - {{ rule.category.name }} - - {% endif %} - - - - - {{ rule.source_url }} - - - - - {% if rule.failed %} - - {% else %} - - {% endif %} - - - - {% if rule.enabled %} - - {% else %} - - {% endif %} - - + + + {% with rule|id_for_label:"rules" as id_for_label %} + {% include "components/form/checkbox.html" with name="rules" value=rule.pk id=id_for_label id_for_label=id_for_label %} + {% endwith %} + + + {{ rule.name }} + + + {% if rule.category %} + {{ rule.category.name }} + {% endif %} + + + {{ rule.source_url }} + + + {% if rule.failed %} + + {% else %} + + {% endif %} + + + {% if rule.enabled %} + + {% else %} + + {% endif %} + + {% endfor %} @@ -107,32 +81,24 @@
    -
    -
  • +
    {% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/subreddit-create.html b/src/newsreader/news/collection/templates/news/collection/views/subreddit-create.html new file mode 100644 index 0000000..6250e4e --- /dev/null +++ b/src/newsreader/news/collection/templates/news/collection/views/subreddit-create.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
    + {% url "news:collection:rules" as cancel_url %} + {% include "components/form/form.html" with form=form title="Add a subreddit" cancel_url=cancel_url confirm_text="Add subrredit" %} +
    +{% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/subreddit-update.html b/src/newsreader/news/collection/templates/news/collection/views/subreddit-update.html new file mode 100644 index 0000000..0099e3b --- /dev/null +++ b/src/newsreader/news/collection/templates/news/collection/views/subreddit-update.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% load static i18n %} + +{% block content %} +
    + {% if subreddit.error %} + {% trans "Failed to retrieve posts" as title %} + {% include "components/textbox/textbox.html" with title=title body=subreddit.error class="text-section--error" only %} + {% endif %} + + {% url "news:collection:rules" as cancel_url %} + {% include "components/form/form.html" with form=form title="Update subreddit" cancel_url=cancel_url confirm_text="Save subreddit" %} +
    +{% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/twitter/timeline-create.html b/src/newsreader/news/collection/templates/news/collection/views/twitter/timeline-create.html new file mode 100644 index 0000000..7c8eb13 --- /dev/null +++ b/src/newsreader/news/collection/templates/news/collection/views/twitter/timeline-create.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
    + {% url "news:collection:rules" as cancel_url %} + {% include "components/form/form.html" with form=form title="Add a Twitter profile" cancel_url=cancel_url confirm_text="Add profile" %} +
    +{% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/twitter/timeline-update.html b/src/newsreader/news/collection/templates/news/collection/views/twitter/timeline-update.html new file mode 100644 index 0000000..51de47a --- /dev/null +++ b/src/newsreader/news/collection/templates/news/collection/views/twitter/timeline-update.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% load static i18n %} + +{% block content %} +
    + {% if timeline.error %} + {% trans "Failed to retrieve posts" as title %} + {% include "components/textbox/textbox.html" with title=title body=timeline.error class="text-section--error" only %} + {% endif %} + + {% url "news:collection:rules" as cancel_url %} + {% include "components/form/form.html" with form=form title="Update profile" cancel_url=cancel_url confirm_text="Save profile" %} +
    +{% endblock %} diff --git a/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py index 5c6b1e0..0e1bc7e 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py @@ -38,7 +38,7 @@ class CollectionRuleDetailViewTestCase(TestCase): data = response.json() self.assertEquals(response.status_code, 404) - self.assertEquals(data["detail"], "No CollectionRule matches the given query.") + self.assertEquals(data["detail"], "Not found.") def test_post(self): rule = FeedFactory(user=self.user) @@ -64,6 +64,22 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(response.status_code, 200) self.assertEquals(data["name"], "The guardian") + def test_category_change(self): + old_category = CategoryFactory(user=self.user) + new_category = CategoryFactory(user=self.user) + + rule = FeedFactory(name="BBC", category=old_category, user=self.user) + + response = self.client.patch( + reverse("api:news:collection:rules-detail", args=[rule.pk]), + data=json.dumps({"category": absolute_url}), + content_type="application/json", + ) + data = response.json() + + self.assertEquals(response.status_code, 200) + self.assertEquals(data["category"], new_category.pk) + def test_identifier_cannot_be_changed(self): rule = FeedFactory(user=self.user) diff --git a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py index 24d4b9a..5a34dbc 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py @@ -1,10 +1,12 @@ import json -from datetime import datetime, timezone +from datetime import date, datetime, time from django.test import TestCase from django.urls import reverse +import pytz + from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory @@ -151,22 +153,22 @@ class NestedRuleListViewTestCase(TestCase): FeedPostFactory( title="I'm the first post", rule=rule, - publication_date=( - datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc) + publication_date=datetime.combine( + date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc ), ), FeedPostFactory( title="I'm the second post", rule=rule, - publication_date=( - datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc) + publication_date=datetime.combine( + date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc ), ), FeedPostFactory( title="I'm the third post", rule=rule, - publication_date=( - datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc) + publication_date=datetime.combine( + date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc ), ), ] @@ -200,9 +202,9 @@ class NestedRuleListViewTestCase(TestCase): for post in data["results"]: with self.subTest(post=post): - self.assertEqual(post["rule"]["id"], rule.pk) + self.assertEqual(post["rule"], rule.pk) - def test_posts(self): + def test_unread_posts(self): rule = FeedFactory.create(user=self.user) FeedPostFactory.create_batch(size=10, rule=rule, read=False) @@ -210,6 +212,7 @@ class NestedRuleListViewTestCase(TestCase): response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), + {"read": "false"}, ) data = response.json() @@ -220,3 +223,23 @@ class NestedRuleListViewTestCase(TestCase): for post in data["results"]: with self.subTest(post=post): self.assertEqual(post["read"], False) + + def test_read_posts(self): + rule = FeedFactory.create(user=self.user) + + FeedPostFactory.create_batch(size=20, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) + + response = self.client.get( + reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), + {"read": "true"}, + ) + + data = response.json() + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) + + for post in data["results"]: + with self.subTest(post=post): + self.assertEqual(post["read"], True) diff --git a/src/newsreader/news/collection/tests/factories.py b/src/newsreader/news/collection/tests/factories.py index 1f80a98..26f66cc 100644 --- a/src/newsreader/news/collection/tests/factories.py +++ b/src/newsreader/news/collection/tests/factories.py @@ -3,6 +3,7 @@ import factory from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.reddit import REDDIT_URL class CollectionRuleFactory(factory.django.DjangoModelFactory): @@ -22,3 +23,13 @@ class CollectionRuleFactory(factory.django.DjangoModelFactory): class FeedFactory(CollectionRuleFactory): type = RuleTypeChoices.feed + + +class SubredditFactory(CollectionRuleFactory): + type = RuleTypeChoices.subreddit + website_url = REDDIT_URL + + +class TwitterTimelineFactory(CollectionRuleFactory): + type = RuleTypeChoices.twitter_timeline + screen_name = factory.Faker("user_name") diff --git a/src/newsreader/news/collection/tests/favicon/builder/tests.py b/src/newsreader/news/collection/tests/favicon/builder/tests.py index a670f8a..d21f77e 100644 --- a/src/newsreader/news/collection/tests/favicon/builder/tests.py +++ b/src/newsreader/news/collection/tests/favicon/builder/tests.py @@ -4,14 +4,7 @@ from django.test import TestCase from newsreader.news.collection.favicon import FaviconBuilder from newsreader.news.collection.tests.factories import CollectionRuleFactory -from newsreader.news.collection.tests.favicon.builder.mocks import ( - mock_with_multiple_icons, - mock_with_other_url, - mock_with_weird_path, - mock_without_header, - mock_without_url, - simple_mock, -) +from newsreader.news.collection.tests.favicon.builder.mocks import * class FaviconBuilderTestCase(TestCase): diff --git a/src/newsreader/news/collection/tests/favicon/collector/mocks.py b/src/newsreader/news/collection/tests/favicon/collector/mocks.py index d9b65f1..3318ffd 100644 --- a/src/newsreader/news/collection/tests/favicon/collector/mocks.py +++ b/src/newsreader/news/collection/tests/favicon/collector/mocks.py @@ -44,7 +44,7 @@ feed_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Trump's genocidal taunts will not end Iran - Zarif", + "value": "Trump's genocidal taunts will not " "end Iran - Zarif", }, }, { @@ -83,7 +83,7 @@ feed_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Huawei's Android loss: How it affects you", + "value": "Huawei's Android loss: How it " "affects you", }, }, { @@ -124,7 +124,7 @@ feed_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Birmingham head teacher threatened over LGBT lessons", + "value": "Birmingham head teacher threatened " "over LGBT lessons", }, }, ], @@ -134,6 +134,7 @@ feed_mock = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "link": "https://www.bbc.co.uk/news/", "links": [ diff --git a/src/newsreader/news/collection/tests/favicon/collector/tests.py b/src/newsreader/news/collection/tests/favicon/collector/tests.py index 23dea56..cb73a7c 100644 --- a/src/newsreader/news/collection/tests/favicon/collector/tests.py +++ b/src/newsreader/news/collection/tests/favicon/collector/tests.py @@ -6,6 +6,7 @@ from bs4 import BeautifulSoup from newsreader.news.collection.exceptions import ( StreamDeniedException, + StreamException, StreamForbiddenException, StreamNotFoundException, StreamParseException, diff --git a/src/newsreader/news/collection/tests/feed/builder/mocks.py b/src/newsreader/news/collection/tests/feed/builder/mocks.py index 8fedad3..2ec57fd 100644 --- a/src/newsreader/news/collection/tests/feed/builder/mocks.py +++ b/src/newsreader/news/collection/tests/feed/builder/mocks.py @@ -108,6 +108,8 @@ mock_without_url = { "id": "https://www.bbc.co.uk/news/world-us-canada-48338168", "published": "Mon, 20 May 2019 16:07:37 GMT", "published_parsed": struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), + "published": None, + "published_parsed": None, "summary": "Foreign Minister Mohammad Javad Zarif says the US " "president should try showing Iranians some respect.", "title": "Trump's 'genocidal taunts' will not end Iran - Zarif", diff --git a/src/newsreader/news/collection/tests/feed/builder/tests.py b/src/newsreader/news/collection/tests/feed/builder/tests.py index 3a32e76..7f4edf0 100644 --- a/src/newsreader/news/collection/tests/feed/builder/tests.py +++ b/src/newsreader/news/collection/tests/feed/builder/tests.py @@ -1,7 +1,10 @@ -from datetime import datetime, timezone +from datetime import datetime from unittest.mock import Mock from django.test import TestCase +from django.utils import timezone + +import pytz from freezegun import freeze_time @@ -10,23 +13,7 @@ from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.models import Post from newsreader.news.core.tests.factories import FeedPostFactory -from .mocks import ( - mock_with_html, - mock_with_long_author, - mock_with_long_exotic_title, - mock_with_long_title, - mock_with_longer_content_detail, - mock_with_multiple_content_detail, - mock_with_shorter_content_detail, - mock_with_update_entries, - mock_without_author, - mock_without_body, - mock_without_entries, - mock_without_identifier, - mock_without_publish_date, - mock_without_url, - multiple_mock, -) +from .mocks import * @freeze_time("2019-10-30 12:30:00") @@ -48,7 +35,7 @@ class FeedBuilderTestCase(TestCase): post = posts[0] publication_date = datetime( - 2019, 5, 20, hour=16, minute=32, second=38, tzinfo=timezone.utc + 2019, 5, 20, hour=16, minute=32, second=38, tzinfo=pytz.utc ) self.assertEqual( @@ -72,7 +59,7 @@ class FeedBuilderTestCase(TestCase): post = posts[1] publication_date = datetime( - 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc + 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc ) self.assertEqual( @@ -107,7 +94,7 @@ class FeedBuilderTestCase(TestCase): post = posts[0] publication_date = datetime( - 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc + 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc ) self.assertEqual(post.publication_date, publication_date) @@ -122,7 +109,7 @@ class FeedBuilderTestCase(TestCase): post = posts[1] publication_date = datetime( - 2019, 5, 20, hour=12, minute=19, second=19, tzinfo=timezone.utc + 2019, 5, 20, hour=12, minute=19, second=19, tzinfo=pytz.utc ) self.assertEqual(post.publication_date, publication_date) @@ -146,7 +133,7 @@ class FeedBuilderTestCase(TestCase): self.assertEqual( post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30" ) - self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual(post.created, timezone.now()) self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", @@ -157,7 +144,7 @@ class FeedBuilderTestCase(TestCase): self.assertEqual( post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30" ) - self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual(post.created, timezone.now()) self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" ) @@ -175,7 +162,7 @@ class FeedBuilderTestCase(TestCase): post = posts[0] - self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual(post.created, timezone.now()) self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", @@ -183,7 +170,7 @@ class FeedBuilderTestCase(TestCase): post = posts[1] - self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual(post.created, timezone.now()) self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" ) @@ -235,7 +222,7 @@ class FeedBuilderTestCase(TestCase): post = posts[0] - self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual(post.created, timezone.now()) self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", @@ -244,7 +231,7 @@ class FeedBuilderTestCase(TestCase): post = posts[1] - self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual(post.created, timezone.now()) self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" ) diff --git a/src/newsreader/news/collection/tests/feed/client/mocks.py b/src/newsreader/news/collection/tests/feed/client/mocks.py index 185f03d..05283a4 100644 --- a/src/newsreader/news/collection/tests/feed/client/mocks.py +++ b/src/newsreader/news/collection/tests/feed/client/mocks.py @@ -42,7 +42,7 @@ simple_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Trump's 'genocidal taunts' will not end Iran - Zarif", + "value": "Trump's 'genocidal taunts' will not " "end Iran - Zarif", }, } ], @@ -52,6 +52,7 @@ simple_mock = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "links": [ { diff --git a/src/newsreader/news/collection/tests/feed/collector/mocks.py b/src/newsreader/news/collection/tests/feed/collector/mocks.py index 7999f99..8ff19b9 100644 --- a/src/newsreader/news/collection/tests/feed/collector/mocks.py +++ b/src/newsreader/news/collection/tests/feed/collector/mocks.py @@ -42,7 +42,7 @@ multiple_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Trump's 'genocidal taunts' will not end Iran - Zarif", + "value": "Trump's 'genocidal taunts' will not " "end Iran - Zarif", }, }, { @@ -81,7 +81,7 @@ multiple_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Huawei's Android loss: How it affects you", + "value": "Huawei's Android loss: How it " "affects you", }, }, { @@ -122,7 +122,7 @@ multiple_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Birmingham head teacher threatened over LGBT lessons", + "value": "Birmingham head teacher threatened " "over LGBT lessons", }, }, ], @@ -132,6 +132,7 @@ multiple_mock = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "links": [ { @@ -157,6 +158,7 @@ empty_mock = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "links": [ { @@ -212,7 +214,7 @@ duplicate_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Trump's 'genocidal taunts' will not end Iran - Zarif", + "value": "Trump's 'genocidal taunts' will not " "end Iran - Zarif", }, }, { @@ -250,7 +252,7 @@ duplicate_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Huawei's Android loss: How it affects you", + "value": "Huawei's Android loss: How it " "affects you", }, }, { @@ -290,7 +292,7 @@ duplicate_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Birmingham head teacher threatened over LGBT lessons", + "value": "Birmingham head teacher threatened " "over LGBT lessons", }, }, ], @@ -300,6 +302,7 @@ duplicate_mock = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "links": [ { @@ -356,7 +359,7 @@ multiple_update_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Trump's 'genocidal taunts' will not end Iran - Zarif", + "value": "Trump's 'genocidal taunts' will not " "end Iran - Zarif", }, }, { @@ -395,7 +398,7 @@ multiple_update_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Huawei's Android loss: How it affects you", + "value": "Huawei's Android loss: How it " "affects you", }, }, { @@ -436,7 +439,7 @@ multiple_update_mock = { "base": "http://feeds.bbci.co.uk/news/rss.xml", "language": None, "type": "text/plain", - "value": "Birmingham head teacher threatened over LGBT lessons", + "value": "Birmingham head teacher threatened " "over LGBT lessons", }, }, ], @@ -446,6 +449,7 @@ multiple_update_mock = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "links": [ { diff --git a/src/newsreader/news/collection/tests/feed/collector/tests.py b/src/newsreader/news/collection/tests/feed/collector/tests.py index f44137a..a7f3573 100644 --- a/src/newsreader/news/collection/tests/feed/collector/tests.py +++ b/src/newsreader/news/collection/tests/feed/collector/tests.py @@ -1,15 +1,20 @@ -from datetime import datetime, timezone +from datetime import date, datetime, time from time import struct_time from unittest.mock import Mock, patch from django.test import TestCase +from django.utils import timezone + +import pytz from freezegun import freeze_time from newsreader.news.collection.exceptions import ( StreamDeniedException, + StreamException, StreamForbiddenException, StreamNotFoundException, + StreamParseException, StreamTimeOutException, ) from newsreader.news.collection.feed import FeedCollector @@ -44,10 +49,10 @@ class FeedCollectorTestCase(TestCase): rule.refresh_from_db() - self.assertEqual(Post.objects.count(), 3) - self.assertEqual(rule.succeeded, True) - self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc)) - self.assertEqual(rule.error, None) + self.assertEquals(Post.objects.count(), 3) + self.assertEquals(rule.succeeded, True) + self.assertEquals(rule.last_run, timezone.now()) + self.assertEquals(rule.error, None) def test_emtpy_batch(self): self.mocked_fetch.return_value = Mock() @@ -60,10 +65,10 @@ class FeedCollectorTestCase(TestCase): rule.refresh_from_db() - self.assertEqual(Post.objects.count(), 0) - self.assertEqual(rule.succeeded, True) - self.assertEqual(rule.error, None) - self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc)) + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, True) + self.assertEquals(rule.error, None) + self.assertEquals(rule.last_run, timezone.now()) def test_not_found(self): self.mocked_fetch.side_effect = StreamNotFoundException @@ -74,14 +79,14 @@ class FeedCollectorTestCase(TestCase): rule.refresh_from_db() - self.assertEqual(Post.objects.count(), 0) - self.assertEqual(rule.succeeded, False) - self.assertEqual(rule.error, "Stream not found") + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream not found") def test_denied(self): self.mocked_fetch.side_effect = StreamDeniedException - old_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc) + old_run = timezone.make_aware(datetime(2019, 10, 30, 12, 30)) rule = FeedFactory(last_run=old_run) collector = FeedCollector() @@ -89,15 +94,15 @@ class FeedCollectorTestCase(TestCase): rule.refresh_from_db() - self.assertEqual(Post.objects.count(), 0) - self.assertEqual(rule.succeeded, False) - self.assertEqual(rule.error, "Stream does not have sufficient permissions") - self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc)) + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream does not have sufficient permissions") + self.assertEquals(rule.last_run, timezone.now()) def test_forbidden(self): self.mocked_fetch.side_effect = StreamForbiddenException - old_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc) + old_run = pytz.utc.localize(datetime(2019, 10, 30, 12, 30)) rule = FeedFactory(last_run=old_run) collector = FeedCollector() @@ -105,15 +110,17 @@ class FeedCollectorTestCase(TestCase): rule.refresh_from_db() - self.assertEqual(Post.objects.count(), 0) - self.assertEqual(rule.succeeded, False) - self.assertEqual(rule.error, "Stream forbidden") - self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc)) + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream forbidden") + self.assertEquals(rule.last_run, timezone.now()) def test_timed_out(self): self.mocked_fetch.side_effect = StreamTimeOutException - last_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc) + last_run = timezone.make_aware( + datetime.combine(date=date(2019, 10, 30), time=time(12, 30)) + ) rule = FeedFactory(last_run=last_run) collector = FeedCollector() @@ -121,11 +128,11 @@ class FeedCollectorTestCase(TestCase): rule.refresh_from_db() - self.assertEqual(Post.objects.count(), 0) - self.assertEqual(rule.succeeded, False) - self.assertEqual(rule.error, "Stream timed out") - self.assertEqual( - rule.last_run, datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc) + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream timed out") + self.assertEquals( + rule.last_run, pytz.utc.localize(datetime(2019, 10, 30, 12, 30)) ) def test_duplicates(self): @@ -134,10 +141,10 @@ class FeedCollectorTestCase(TestCase): rule = FeedFactory() aware_datetime = build_publication_date( - struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)) + struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), pytz.utc ) - FeedPostFactory( + first_post = FeedPostFactory( url="https://www.bbc.co.uk/news/world-us-canada-48338168", title="Trump's 'genocidal taunts' will not end Iran - Zarif", body="Foreign Minister Mohammad Javad Zarif says the US " @@ -147,10 +154,10 @@ class FeedCollectorTestCase(TestCase): ) aware_datetime = build_publication_date( - struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)) + struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)), pytz.utc ) - FeedPostFactory( + second_post = FeedPostFactory( url="https://www.bbc.co.uk/news/technology-48334739", title="Huawei's Android loss: How it affects you", body="Google's move to end business ties with Huawei will " @@ -160,10 +167,10 @@ class FeedCollectorTestCase(TestCase): ) aware_datetime = build_publication_date( - struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)) + struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)), pytz.utc ) - FeedPostFactory( + third_post = FeedPostFactory( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Police are investigating the messages while an MP " @@ -178,10 +185,10 @@ class FeedCollectorTestCase(TestCase): rule.refresh_from_db() - self.assertEqual(Post.objects.count(), 3) - self.assertEqual(rule.succeeded, True) - self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc)) - self.assertEqual(rule.error, None) + self.assertEquals(Post.objects.count(), 3) + self.assertEquals(rule.succeeded, True) + self.assertEquals(rule.last_run, timezone.now()) + self.assertEquals(rule.error, None) def test_items_with_identifiers_get_updated(self): self.mocked_parse.return_value = multiple_update_mock @@ -192,7 +199,7 @@ class FeedCollectorTestCase(TestCase): url="https://www.bbc.co.uk/", title="Trump", body="Foreign Minister Mohammad Javad Zarif", - publication_date=datetime.now(tz=timezone.utc), + publication_date=timezone.now(), rule=rule, ) @@ -201,7 +208,7 @@ class FeedCollectorTestCase(TestCase): url="https://www.bbc.co.uk/", title="Huawei's Android loss: How it affects you", body="Google's move to end business ties with Huawei will", - publication_date=datetime.now(tz=timezone.utc), + publication_date=timezone.now(), rule=rule, ) @@ -210,7 +217,7 @@ class FeedCollectorTestCase(TestCase): url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Police are investigating the messages while an MP", - publication_date=datetime.now(tz=timezone.utc), + publication_date=timezone.now(), rule=rule, ) @@ -222,17 +229,19 @@ class FeedCollectorTestCase(TestCase): second_post.refresh_from_db() third_post.refresh_from_db() - self.assertEqual(Post.objects.count(), 3) - self.assertEqual(rule.succeeded, True) - self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc)) - self.assertEqual(rule.error, None) + self.assertEquals(Post.objects.count(), 3) + self.assertEquals(rule.succeeded, True) + self.assertEquals(rule.last_run, timezone.now()) + self.assertEquals(rule.error, None) - self.assertEqual( + self.assertEquals( first_post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif" ) - self.assertEqual(second_post.title, "Huawei's Android loss: How it affects you") + self.assertEquals( + second_post.title, "Huawei's Android loss: How it affects you" + ) - self.assertEqual( + self.assertEquals( third_post.title, "Birmingham head teacher threatened over LGBT lessons" ) diff --git a/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py b/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py index a1838ea..941de66 100644 --- a/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py +++ b/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py @@ -19,7 +19,7 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_with_remote_identifiers(self): rule = FeedFactory() - FeedPostFactory.create( + existing_post = FeedPostFactory.create( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule ) @@ -54,7 +54,7 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_with_different_remote_identifiers(self): rule = FeedFactory() - FeedPostFactory( + existing_post = FeedPostFactory( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", url="https://bbc.com", title="New post", @@ -100,7 +100,7 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_in_recent_database(self): rule = FeedFactory() - FeedPostFactory( + existing_post = FeedPostFactory( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", @@ -189,7 +189,7 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_outside_time_slot(self): rule = FeedFactory() - FeedPostFactory( + existing_post = FeedPostFactory( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", diff --git a/src/newsreader/news/collection/tests/feed/stream/mocks.py b/src/newsreader/news/collection/tests/feed/stream/mocks.py index e8d6856..4218355 100644 --- a/src/newsreader/news/collection/tests/feed/stream/mocks.py +++ b/src/newsreader/news/collection/tests/feed/stream/mocks.py @@ -73,12 +73,12 @@ simple_mock_parsed = { "not think face coverings should be " "mandatory in shops in England.", }, - "title": "Coronavirus: I trust people's sense on face masks - Gove", + "title": "Coronavirus: I trust people's sense on face masks - " "Gove", "title_detail": { "base": "", "language": None, "type": "text/plain", - "value": "Coronavirus: I trust people's sense on face masks - Gove", + "value": "Coronavirus: I trust people's sense " "on face masks - Gove", }, }, { @@ -109,7 +109,7 @@ simple_mock_parsed = { "base": "", "language": None, "type": "text/plain", - "value": "Farm outbreak leads 200 to self isolate", + "value": "Farm outbreak leads 200 to self " "isolate", }, }, { @@ -137,12 +137,12 @@ simple_mock_parsed = { "talks on tackling people " "smuggling.", }, - "title": "English Channel search operation after migrant crossings", + "title": "English Channel search operation after migrant " "crossings", "title_detail": { "base": "", "language": None, "type": "text/plain", - "value": "English Channel search operation after migrant crossings", + "value": "English Channel search operation " "after migrant crossings", }, }, ], @@ -206,7 +206,6 @@ simple_mock_parsed = { "updated": "Sun, 12 Jul 2020 17:21:20 GMT", "updated_parsed": struct_time((2020, 7, 12, 17, 21, 20, 6, 194, 0)), }, - "headers": {}, "namespaces": { "": "http://www.w3.org/2005/Atom", "content": "http://purl.org/rss/1.0/modules/content/", diff --git a/src/newsreader/news/collection/tests/mocks.py b/src/newsreader/news/collection/tests/mocks.py index 9857bfa..574d3a5 100644 --- a/src/newsreader/news/collection/tests/mocks.py +++ b/src/newsreader/news/collection/tests/mocks.py @@ -17,6 +17,7 @@ simple_feed_mock = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "link": "https://www.bbc.co.uk/news/", "links": [ @@ -42,6 +43,7 @@ feed_mock_without_link = { "link": "https://www.bbc.co.uk/news/", "title": "BBC News - Home", "language": "en-gb", + "link": "https://www.bbc.co.uk/news/", }, "title": "BBC News - Home", }, diff --git a/src/newsreader/news/collection/tests/reddit/__init__.py b/src/newsreader/news/collection/tests/reddit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/reddit/builder/__init__.py b/src/newsreader/news/collection/tests/reddit/builder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/reddit/builder/mocks.py b/src/newsreader/news/collection/tests/reddit/builder/mocks.py new file mode 100644 index 0000000..f10bbaf --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/builder/mocks.py @@ -0,0 +1,5256 @@ +simple_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 8, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 9, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 2, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 2, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_gr7k5", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Here's a feature Linux could borrow from BSD: in-kernel debugger with built-in hangman game", + "link_flair_richtext": [{"e": "text", "t": "Fluff"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hngs71", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.9, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 158, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Fluff", + "can_mod_post": False, + "score": 158, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594242629.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/wmc8tp2ium951.jpg", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "af8918be-6777-11e7-8273-0e925d908786", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#9a2bff", + "id": "hngs71", + "is_robot_indexable": True, + "report_reasons": None, + "author": "the_humeister", + "discussion_type": None, + "num_comments": 21, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hngs71/heres_a_feature_linux_could_borrow_from_bsd/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/wmc8tp2ium951.jpg", + "subreddit_subscribers": 544037, + "created_utc": 1594213829.0, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_k9f35", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "KeePassXC 2.6.0 released", + "link_flair_richtext": [{"e": "text", "t": "Software Release"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hngsj8", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.97, + "author_flair_background_color": "transparent", + "subreddit_type": "public", + "ups": 151, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "fa602e36-cdf6-11e8-93c9-0e41ac35f4cc", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Software Release", + "can_mod_post": False, + "score": 151, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [ + { + "a": ":ubuntu:", + "e": "emoji", + "u": "https://emoji.redditmedia.com/uwmddx7qqpr11_t5_2qh1a/ubuntu", + } + ], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594242666.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "keepassxc.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://keepassxc.org/blog/2020-07-07-2.6.0-released/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "904ea3e4-6748-11e7-b925-0ef3dfbb807a", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": ":ubuntu:", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#349e48", + "id": "hngsj8", + "is_robot_indexable": True, + "report_reasons": None, + "author": "nixcraft", + "discussion_type": None, + "num_comments": 46, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/linux/comments/hngsj8/keepassxc_260_released/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://keepassxc.org/blog/2020-07-07-2.6.0-released/", + "subreddit_subscribers": 544037, + "created_utc": 1594213866.0, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [{"e": "text", "t": "Popular Application"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd7cy", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.95, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 226, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Popular Application", + "can_mod_post": False, + "score": 226, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "libreoffice", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/libreoffice", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd6yo", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.96, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 29, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "News", + "can_mod_post": False, + "score": 29, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594224961.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "dc82ac98-bafb-11e4-9f88-22000b310327", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2s4nt", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnd6yo", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 38, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/libreoffice/comments/hnd6yo/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 4669, + "created_utc": 1594196161.0, + "num_crossposts": 2, + "media": None, + "is_video": False, + } + ], + "created": 1594225018.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "7127ec98-5859-11e8-9488-0e8717893ec8", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#0aa18f", + "id": "hnd7cy", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 120, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hnd6yo", + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnd7cy/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 544037, + "created_utc": 1594196218.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} + +empty_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [], + "after": "t3_hmytic", + "before": None, + }, +} + +unknown_mock = { + "kind": "Comment", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "after": "t3_hmytic", + "before": None, + }, +} + +unsanitized_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [{"e": "text", "t": "Popular Application"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd7cy", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.95, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 226, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Popular Application", + "can_mod_post": False, + "score": 226, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "libreoffice", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/libreoffice", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd6yo", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.96, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 29, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "News", + "can_mod_post": False, + "score": 29, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594224961.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "dc82ac98-bafb-11e4-9f88-22000b310327", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2s4nt", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnd6yo", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 38, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/libreoffice/comments/hnd6yo/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 4669, + "created_utc": 1594196161.0, + "num_crossposts": 2, + "media": None, + "is_video": False, + } + ], + "created": 1594225018.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": "
    ", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "7127ec98-5859-11e8-9488-0e8717893ec8", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#0aa18f", + "id": "hnd7cy", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 120, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hnd6yo", + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnd7cy/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 544037, + "created_utc": 1594196218.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + } + ], + "after": "t3_hmytic", + "before": None, + }, +} + +author_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [{"e": "text", "t": "Popular Application"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd7cy", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.95, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 226, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Popular Application", + "can_mod_post": False, + "score": 226, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "libreoffice", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/libreoffice", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd6yo", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.96, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 29, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "News", + "can_mod_post": False, + "score": 29, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594224961.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "dc82ac98-bafb-11e4-9f88-22000b310327", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2s4nt", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnd6yo", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 38, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/libreoffice/comments/hnd6yo/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 4669, + "created_utc": 1594196161.0, + "num_crossposts": 2, + "media": None, + "is_video": False, + } + ], + "created": 1594225018.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": "
    ", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "7127ec98-5859-11e8-9488-0e8717893ec8", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#0aa18f", + "id": "hnd7cy", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZeroTheQuantumZeroTheQuantumZero", + "discussion_type": None, + "num_comments": 120, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hnd6yo", + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnd7cy/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 544037, + "created_utc": 1594196218.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + } + ], + "after": "t3_hmytic", + "before": None, + }, +} + +title_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal EditionBoard statement on the LibreOffice 7.0 RC "Personal Edition" label" labelBoard statement on the LibreOffice 7.0 RC "PersBoard statement on the LibreOffice 7.0 RC "Personal Edition" labelonal Edition" label', + "link_flair_richtext": [{"e": "text", "t": "Popular Application"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd7cy", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.95, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 226, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Popular Application", + "can_mod_post": False, + "score": 226, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "libreoffice", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/libreoffice", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd6yo", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.96, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 29, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "News", + "can_mod_post": False, + "score": 29, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594224961.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "dc82ac98-bafb-11e4-9f88-22000b310327", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2s4nt", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnd6yo", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 38, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/libreoffice/comments/hnd6yo/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 4669, + "created_utc": 1594196161.0, + "num_crossposts": 2, + "media": None, + "is_video": False, + } + ], + "created": 1594225018.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": "
    ", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "7127ec98-5859-11e8-9488-0e8717893ec8", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#0aa18f", + "id": "hnd7cy", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZeroTheQuantumZeroTheQuantumZero", + "discussion_type": None, + "num_comments": 120, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hnd6yo", + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnd7cy/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 544037, + "created_utc": 1594196218.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + } + ], + "after": "t3_hmytic", + "before": None, + }, +} + +duplicate_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 8, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 9, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 2, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 2, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 8, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 9, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} + +image_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "SamLynn79", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_6c9cj", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594777552.0, + "created_utc": 1594748752.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 1, + "gildings": {"gid_2": 1}, + "hidden": False, + "hide_score": False, + "id": "hr64xh", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": True, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr64xh", + "no_follow": False, + "num_comments": 579, + "num_crossposts": 2, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "xWBh4hObZx0zmG_IDOHBLNN-_NZzEss2dAgm1sm9p1w", + "resolutions": [ + { + "height": 135, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=108&crop=smart&auto=webp&s=5374b8f3dff520eba8cf97b589ebc67206f130dc", + "width": 108, + }, + { + "height": 270, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=216&crop=smart&auto=webp&s=09d937a8db6f843d9fd34ee024cdfc6432dc0a13", + "width": 216, + }, + { + "height": 400, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=320&crop=smart&auto=webp&s=9ba3654c12cb54f6d9c2dce1b07c80ecd6ca9d06", + "width": 320, + }, + { + "height": 800, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=640&crop=smart&auto=webp&s=8c53747ae0f92b65fdd41f3aab60ebb8f8d4b1ca", + "width": 640, + }, + { + "height": 1200, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=960&crop=smart&auto=webp&s=5668a626da6cd69e23b6c01587783c6cc5817bea", + "width": 960, + }, + { + "height": 1350, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=1080&crop=smart&auto=webp&s=8fdd61aed8718109f3739cb532d96be31192b9a0", + "width": 1080, + }, + ], + "source": { + "height": 1800, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?auto=webp&s=17b817b8d0e35bddc7f605d242cd7d116ef8e235", + "width": 1440, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 23419, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/0X39S2jBL66zQCUbJAtlRKeswI8uUxf3-7vmog0VLjc.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Ya’ll, I just can’t... this is my " + "son, Judah. My wife and I have no " + "idea how we created such a " + "beautiful child.", + "top_awarded_type": None, + "total_awards_received": 4, + "treatment_tags": [], + "ups": 23419, + "upvote_ratio": 0.72, + "url": "https://i.redd.it/cm2qybia1va51.jpg", + "url_overridden_by_dest": "https://i.redd.it/cm2qybia1va51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "0_GG_0", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_70k94sn8", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594771808.0, + "created_utc": 1594743008.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr4bxo", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr4bxo", + "no_follow": False, + "num_comments": 248, + "num_crossposts": 4, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr4bxo/just_thought_yall_would_enjoy_my_goat_dressed_as/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "TSXyc6ZJGdCcHk7-wuWnJdVpqsa_t8hmVd4k_e3ofCA", + "resolutions": [ + { + "height": 144, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=108&crop=smart&auto=webp&s=ed5a11a7637acc66de48e30fd51d5019fa0c69f7", + "width": 108, + }, + { + "height": 288, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=216&crop=smart&auto=webp&s=a812bec268d8ea31dbb9dfe696e0798490538f5a", + "width": 216, + }, + { + "height": 426, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=320&crop=smart&auto=webp&s=1be4e3bdea19243b0a627bacb4c9e04f2d3569a7", + "width": 320, + }, + { + "height": 853, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=640&crop=smart&auto=webp&s=e73755c3f0b27bb0435d07aa60b32e091bed7957", + "width": 640, + }, + { + "height": 1280, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=960&crop=smart&auto=webp&s=8ab6972fffc4786503284a0253e91e9104f2d01e", + "width": 960, + }, + { + "height": 1440, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=1080&crop=smart&auto=webp&s=a1e554889179a7599786985679304fda706d83d6", + "width": 1080, + }, + ], + "source": { + "height": 4032, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?auto=webp&s=3eefdef653e0a3a8a10090b804f0888ee6a1a163", + "width": 3024, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 16684, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/h3Ylp4kb0uJzAsST4ZZGsGN8WGxK4wjK2XrM9uUH5uc.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Just thought y’all would enjoy my " + "goat dressed as a tractor", + "top_awarded_type": None, + "total_awards_received": 2, + "treatment_tags": [], + "ups": 16684, + "upvote_ratio": 0.98, + "url": "https://i.redd.it/4udujbu6kua51.jpg", + "url_overridden_by_dest": "https://i.redd.it/4udujbu6kua51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "Mechanic619", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_4ptrdtz5", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594760700.0, + "created_utc": 1594731900.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {"gid_1": 1}, + "hidden": False, + "hide_score": False, + "id": "hr14y5", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr14y5", + "no_follow": False, + "num_comments": 1439, + "num_crossposts": 20, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr14y5/mosque_security_on_patrol/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "Qs_FmhJgYT8GWyxmDQ8kjBCs_w2V_77cvHvdqLJ7i4s", + "resolutions": [ + { + "height": 135, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=108&crop=smart&auto=webp&s=cf4c24ef4f9be86d186c143296bd1e14f15f960a", + "width": 108, + }, + { + "height": 270, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=216&crop=smart&auto=webp&s=308e2367a849334c32b579265ed738d9937bed71", + "width": 216, + }, + { + "height": 400, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=320&crop=smart&auto=webp&s=bc890f054dc34bb3f8607a70d088926afe113ff1", + "width": 320, + }, + { + "height": 800, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=640&crop=smart&auto=webp&s=e23a9bc2d8d1ac6ccefab7f30cfa9def741aaa25", + "width": 640, + }, + { + "height": 1201, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=960&crop=smart&auto=webp&s=4d294d1626046d27edc2a281c21ab10502b9ca4c", + "width": 960, + }, + { + "height": 1351, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=1080&crop=smart&auto=webp&s=a801e5d9d703204e8b1497d3038d6405b2ed1157", + "width": 1080, + }, + ], + "source": { + "height": 1413, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?auto=webp&s=f4e87e2ad0f0e40ca4f7a08c2a894b234601f3ce", + "width": 1129, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 89133, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/GGHjIElMHDgefR0UdMXVk8CHeDUBhuZMY_QHjls4ynA.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Mosque security on patrol", + "top_awarded_type": None, + "total_awards_received": 3, + "treatment_tags": [], + "ups": 89133, + "upvote_ratio": 0.93, + "url": "https://i.redd.it/jk08ge66nta51.jpg", + "url_overridden_by_dest": "https://i.redd.it/jk08ge66nta51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "Amnesia19", + "author_cakeday": True, + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_1rqe7gk1", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594765470.0, + "created_utc": 1594736670.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr2fv0", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr2fv0", + "no_follow": False, + "num_comments": 71, + "num_crossposts": 1, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr2fv0/the_look_my_dog_gives_my_grandpa/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "v0BbkKy6haXmUxmHz4oXygoR0E-cHkvZDACWL_s7STw", + "resolutions": [ + { + "height": 144, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=108&crop=smart&auto=webp&s=4e65e8ff55c02de0ebe79763c91fe43f51216717", + "width": 108, + }, + { + "height": 288, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=216&crop=smart&auto=webp&s=e2006e5fe7ac43f911c17dc7f185f33db24e3b52", + "width": 216, + }, + { + "height": 426, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=320&crop=smart&auto=webp&s=3dad39d5e48a1b176f7e87b2dd110fb0044b32d7", + "width": 320, + }, + { + "height": 853, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=640&crop=smart&auto=webp&s=2f8e86a3feca27a23a72d10b92aba1b79b80f7be", + "width": 640, + }, + { + "height": 1280, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=960&crop=smart&auto=webp&s=5ecdd44b728031f8e109f41f99841a1d6c8e86c8", + "width": 960, + }, + { + "height": 1440, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=1080&crop=smart&auto=webp&s=49555499040c0ac9958dabd98cbe4e90c054b2a7", + "width": 1080, + }, + ], + "source": { + "height": 4032, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?auto=webp&s=443e98e46a8a096e426ebdc256c45682f46ebe2a", + "width": 3024, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 13614, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/RWRuGJ7ZyBtjO6alY1vbc65TQzgng8RFRWnPG7WUkhE.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "The look my dog gives my grandpa", + "top_awarded_type": None, + "total_awards_received": 0, + "treatment_tags": [], + "ups": 13614, + "upvote_ratio": 0.99, + "url": "https://i.redd.it/y6q7bgzc1ua51.jpg", + "url_overridden_by_dest": "https://i.redd.it/y6q7bgzc1ua51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +external_image_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "Captainbuttsreads", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_5qaat4af", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594770844.0, + "created_utc": 1594742044.0, + "crosspost_parent": "t3_gc6eq2", + "crosspost_parent_list": [], + "discussion_type": None, + "distinguished": None, + "domain": "gfycat.com", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr41am", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": False, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr41am", + "no_follow": False, + "num_comments": 45, + "num_crossposts": 0, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr41am/excited_cows_have_a_new_brush/", + "pinned": False, + "post_hint": "link", + "preview": { + "enabled": False, + "images": [ + { + "id": "l5tVSe6B4QDc7wk6Z9WfCXr20D_rAOHerf6i0N53nNc", + "resolutions": [ + { + "height": 108, + "url": "https://external-preview.redd.it/R51JAzaGbva91vYxn9uWL3NwCzWJW5mrdVxb1idjtBg.jpg?width=108&crop=smart&auto=webp&s=f908e1fb9403194a31f9a0c1f056f59e0718201e", + "width": 108, + }, + { + "height": 216, + "url": "https://external-preview.redd.it/R51JAzaGbva91vYxn9uWL3NwCzWJW5mrdVxb1idjtBg.jpg?width=216&crop=smart&auto=webp&s=de377df68832a52419d83c06ea74a13de28b96e0", + "width": 216, + }, + ], + "source": { + "height": 250, + "url": "https://external-preview.redd.it/R51JAzaGbva91vYxn9uWL3NwCzWJW5mrdVxb1idjtBg.jpg?auto=webp&s=b4166cb5a350e6d0197381cdf8db702f8a760493", + "width": 250, + }, + "variants": {}, + } + ], + "reddit_video_preview": { + "dash_url": "https://v.redd.it/mimyo7z6ppa51/DASHPlaylist.mpd", + "duration": 33, + "fallback_url": "https://v.redd.it/mimyo7z6ppa51/DASH_480.mp4", + "height": 640, + "hls_url": "https://v.redd.it/mimyo7z6ppa51/HLSPlaylist.m3u8", + "is_gif": True, + "scrubber_media_url": "https://v.redd.it/mimyo7z6ppa51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + }, + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 3219, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": False, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/NKTwvIU2xxoOMpzYNlYYstS2586x64Gi--52N0M-OJY.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Excited cows have a new brush!", + "top_awarded_type": None, + "total_awards_received": 0, + "treatment_tags": [], + "ups": 3219, + "upvote_ratio": 0.99, + "url": "http://gfycat.com/thatalivedogwoodclubgall", + "url_overridden_by_dest": "http://gfycat.com/thatalivedogwoodclubgall", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_78ni2", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallas’s cat kittens", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 93, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_huoldn", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.99, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 1933, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 1933, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://a.thumbs.redditmedia.com/j-D-Z79QQ6tGk0E3SGdb8GzqbLVUY3lu59tDaXbOYl8.jpg", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "image", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1595292144, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.imgur.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.imgur.com/usfMVUJ.jpg", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": False, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?auto=webp&s=2126d34a0134efa94ecab03917944709c8bc3305", + "width": 1024, + "height": 682, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=108&crop=smart&auto=webp&s=710a44f787b98a0a37ca543b7428917ee55b3c46", + "width": 108, + "height": 71, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=216&crop=smart&auto=webp&s=b1bcdd7734a3a569f99fa88c6be9447105e58276", + "width": 216, + "height": 143, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=320&crop=smart&auto=webp&s=1671bf09a7b73d0ca51cf2de884b37d6a3591d6a", + "width": 320, + "height": 213, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=640&crop=smart&auto=webp&s=9fcdddbaeaad13273e0b53a862c73c4fee9f7e3d", + "width": 640, + "height": 426, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=960&crop=smart&auto=webp&s=e531480236c0ae72b78f27dd88f2cedc9f73cccc", + "width": 960, + "height": 639, + }, + ], + "variants": {}, + "id": "oJ9pHVA-JhoodtgNlku8ZQv8FhtadS2r36wGLAriUtY", + } + ], + "enabled": True, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": False, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "huoldn", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Ben_zyl", + "discussion_type": None, + "num_comments": 20, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.imgur.com/usfMVUJ.jpg", + "subreddit_subscribers": 25723833, + "created_utc": 1595263344, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +video_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "TommyLondoner", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_75bis9gi", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594767660.0, + "created_utc": 1594738860.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 1, + "gildings": {"gid_2": 1}, + "hidden": False, + "hide_score": False, + "id": "hr32jf", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/9avhmd5s7ua51/DASHPlaylist.mpd?a=1597351258%2CODVjMjcyMDkzOWE1NDBiNzUwNzVhNDUwYmE0MGNiNzk5MGRmZmZmMzBhZjIzNDAzYzczY2NkNzRjNTgyMjAzNQ%3D%3D&v=1&f=sd", + "duration": 78, + "fallback_url": "https://v.redd.it/9avhmd5s7ua51/DASH_360.mp4?source=fallback", + "height": 428, + "hls_url": "https://v.redd.it/9avhmd5s7ua51/HLSPlaylist.m3u8?a=1597351258%2CNjE4YTA0NjUwZWNmNjhjNTRhNmU4ZjBmNDMyYWYxOGYzZTNkZWM2YjViM2I2ZDZjZWNhYzY0ZGVmOWU0Y2EyYg%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/9avhmd5s7ua51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 258, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr32jf", + "no_follow": False, + "num_comments": 150, + "num_crossposts": 2, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr32jf/this_guy_definitely_loves_his_job/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "dX_mx_ZfJMwVn_pak9ZPQq8rMT_gPkW0_4gOzDxPSHM", + "resolutions": [ + { + "height": 179, + "url": "https://external-preview.redd.it/PMy-Z___DIG6aWnoyEy1VKottxLQFWCRSdHDV1a9N8w.png?width=108&crop=smart&format=pjpg&auto=webp&s=e0b8b68a78a8e9071bf56417ac6589bc8aff7634", + "width": 108, + }, + { + "height": 358, + "url": "https://external-preview.redd.it/PMy-Z___DIG6aWnoyEy1VKottxLQFWCRSdHDV1a9N8w.png?width=216&crop=smart&format=pjpg&auto=webp&s=8668c3c7ccbdacfe3376d8af4b1b49df9d6aec97", + "width": 216, + }, + ], + "source": { + "height": 428, + "url": "https://external-preview.redd.it/PMy-Z___DIG6aWnoyEy1VKottxLQFWCRSdHDV1a9N8w.png?format=pjpg&auto=webp&s=b0b6439fbe01c3f5d1bf1eae54a588cc745d3415", + "width": 258, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 9324, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/9avhmd5s7ua51/DASHPlaylist.mpd?a=1597351258%2CODVjMjcyMDkzOWE1NDBiNzUwNzVhNDUwYmE0MGNiNzk5MGRmZmZmMzBhZjIzNDAzYzczY2NkNzRjNTgyMjAzNQ%3D%3D&v=1&f=sd", + "duration": 78, + "fallback_url": "https://v.redd.it/9avhmd5s7ua51/DASH_360.mp4?source=fallback", + "height": 428, + "hls_url": "https://v.redd.it/9avhmd5s7ua51/HLSPlaylist.m3u8?a=1597351258%2CNjE4YTA0NjUwZWNmNjhjNTRhNmU4ZjBmNDMyYWYxOGYzZTNkZWM2YjViM2I2ZDZjZWNhYzY0ZGVmOWU0Y2EyYg%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/9avhmd5s7ua51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 258, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/ibsS3H5xMLDSVglh8NBYJ4cgIsXuqYVLJWbiYVTykXg.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "This guy definitely loves his job !", + "top_awarded_type": None, + "total_awards_received": 1, + "treatment_tags": [], + "ups": 9324, + "upvote_ratio": 0.96, + "url": "https://v.redd.it/9avhmd5s7ua51", + "url_overridden_by_dest": "https://v.redd.it/9avhmd5s7ua51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "LucileEsparza", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_5loa1v96", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594762969.0, + "created_utc": 1594734169.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr1r00", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/eyvbxaeqtta51/DASHPlaylist.mpd?a=1597351258%2CYjJmMWE3ZGJmM2FhMzVkYzZlNjIzOTAwM2ZmZTBkYjAxMzE0NDY2MDIyNGRhOWViMTViZTE0NTlmMzkzM2JlYg%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback", + "height": 640, + "hls_url": "https://v.redd.it/eyvbxaeqtta51/HLSPlaylist.m3u8?a=1597351258%2CY2JiMmQ0MjliNmE5NTA5MDE3YjAyNmVkYTg2Yjg1YWYwYmJlNDE4ZGM1NjE4ZDU3YjkzYjJlMDE2ZmM4Yzk5MQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/eyvbxaeqtta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr1r00", + "no_follow": False, + "num_comments": 63, + "num_crossposts": 3, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "wrscJ_l9A6Q_Mn1NAg06I4o3W39bbNgTBYg2Xm_Vl8U", + "resolutions": [ + { + "height": 108, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=108&crop=smart&format=pjpg&auto=webp&s=f285ef95065be8a340e1cb7792d80a9640564eb6", + "width": 108, + }, + { + "height": 216, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=216&crop=smart&format=pjpg&auto=webp&s=6d26b4f8d7b16f0f02bc6ce6f35af889b43cf026", + "width": 216, + }, + { + "height": 320, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=320&crop=smart&format=pjpg&auto=webp&s=5d081467da187bd8c24e9c524583513ee6afe388", + "width": 320, + }, + { + "height": 640, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=640&crop=smart&format=pjpg&auto=webp&s=557369f302f18b35284ffaacaccf09986f755187", + "width": 640, + }, + ], + "source": { + "height": 640, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?format=pjpg&auto=webp&s=cb0a79a2effe0323e862fb713dab76b39051afbb", + "width": 640, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 11007, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/eyvbxaeqtta51/DASHPlaylist.mpd?a=1597351258%2CYjJmMWE3ZGJmM2FhMzVkYzZlNjIzOTAwM2ZmZTBkYjAxMzE0NDY2MDIyNGRhOWViMTViZTE0NTlmMzkzM2JlYg%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback", + "height": 640, + "hls_url": "https://v.redd.it/eyvbxaeqtta51/HLSPlaylist.m3u8?a=1597351258%2CY2JiMmQ0MjliNmE5NTA5MDE3YjAyNmVkYTg2Yjg1YWYwYmJlNDE4ZGM1NjE4ZDU3YjkzYjJlMDE2ZmM4Yzk5MQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/eyvbxaeqtta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": False, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/WSBiDcoWPwAgSkt08uCI6TK7v_tdAdHmQHv7TePyTOs.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Cool catt and his clingy girlfriend", + "top_awarded_type": None, + "total_awards_received": 1, + "treatment_tags": [], + "ups": 11007, + "upvote_ratio": 0.99, + "url": "https://v.redd.it/eyvbxaeqtta51", + "url_overridden_by_dest": "https://v.redd.it/eyvbxaeqtta51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "memezzer", + "author_flair_background_color": "", + "author_flair_css_class": "k", + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": "dark", + "author_flair_type": "text", + "author_fullname": "t2_41jaebm4", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594759625.0, + "created_utc": 1594730825.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr0uzh", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/y0mavwswjta51/DASHPlaylist.mpd?a=1597351258%2CYjU1NzFjOTE0YzY2OTdmODk3MGRiMGU4MjdhOGE5ODk2YWNiODQyMGUyOWRhNzI1M2U1MTEyZjBhOWZkZTZmMw%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/y0mavwswjta51/DASH_720.mp4?source=fallback", + "height": 960, + "hls_url": "https://v.redd.it/y0mavwswjta51/HLSPlaylist.m3u8?a=1597351258%2CODk4NTdhMzA3NmY2ZmY2NGQxMmI2ZjcyMzk0ZTFhOTdhOGI4NGQ1NjBiMzNiMmVmZDBhMTQ4MGRkOWJlOWU1YQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/y0mavwswjta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 960, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr0uzh", + "no_follow": False, + "num_comments": 86, + "num_crossposts": 3, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr0uzh/good_pillow/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "neoTdGv5lMArlfu6euGUK_v_O87Lfmdrrz1ePTwzp1w", + "resolutions": [ + { + "height": 108, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=108&crop=smart&format=pjpg&auto=webp&s=dcc1172b7ace007e8c72080519a16a487596d7e2", + "width": 108, + }, + { + "height": 216, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=216&crop=smart&format=pjpg&auto=webp&s=a7968ce1aa34957a7f7103d06a66d4f9df95d437", + "width": 216, + }, + { + "height": 320, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=320&crop=smart&format=pjpg&auto=webp&s=a2302d80948fba08e91db0a10db579341e1df712", + "width": 320, + }, + { + "height": 640, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=640&crop=smart&format=pjpg&auto=webp&s=a8487450d38d14bcdfda2aeb659b453d8b1cacab", + "width": 640, + }, + { + "height": 960, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=960&crop=smart&format=pjpg&auto=webp&s=d371bee68cab49130babe4b890c6323db128c214", + "width": 960, + }, + ], + "source": { + "height": 960, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?format=pjpg&auto=webp&s=ff90de8f0a693afeca69dc85dbecb6af9783c769", + "width": 960, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 13271, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/y0mavwswjta51/DASHPlaylist.mpd?a=1597351258%2CYjU1NzFjOTE0YzY2OTdmODk3MGRiMGU4MjdhOGE5ODk2YWNiODQyMGUyOWRhNzI1M2U1MTEyZjBhOWZkZTZmMw%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/y0mavwswjta51/DASH_720.mp4?source=fallback", + "height": 960, + "hls_url": "https://v.redd.it/y0mavwswjta51/HLSPlaylist.m3u8?a=1597351258%2CODk4NTdhMzA3NmY2ZmY2NGQxMmI2ZjcyMzk0ZTFhOTdhOGI4NGQ1NjBiMzNiMmVmZDBhMTQ4MGRkOWJlOWU1YQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/y0mavwswjta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 960, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": "confidence", + "thumbnail": "https://b.thumbs.redditmedia.com/sxFESWCVsSf4ij5_-a1xdJaFhSU2MjJ5T_TVFbook6Q.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Good pillow", + "top_awarded_type": None, + "total_awards_received": 0, + "treatment_tags": [], + "ups": 13271, + "upvote_ratio": 0.99, + "url": "https://v.redd.it/y0mavwswjta51", + "url_overridden_by_dest": "https://v.redd.it/y0mavwswjta51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "asdfpartyy", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_t0ay0", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594745472.0, + "created_utc": 1594716672.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 1, + "gildings": {"gid_2": 1}, + "hidden": False, + "hide_score": False, + "id": "hqy0ny", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/asj4p03rdsa51/DASHPlaylist.mpd?a=1597351258%2CY2VmYTAyMWNmZjIwZjQ4YTBmMDc5MTRjOTU0NjliZWU3MDE2YTU3NjJiYzQxZWRiODY4ZTc1YWI1NDY4MWIxNA%3D%3D&v=1&f=sd", + "duration": 30, + "fallback_url": "https://v.redd.it/asj4p03rdsa51/DASH_360.mp4?source=fallback", + "height": 360, + "hls_url": "https://v.redd.it/asj4p03rdsa51/HLSPlaylist.m3u8?a=1597351258%2CY2QxM2I4Njk5MmIyOTRiZTBhNDQ2MDg0ZTM2NTllYzBjODBlYjNiNDc1Mzg2ODIxNDk4MTAzMzYyNzlmNjI1NQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/asj4p03rdsa51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hqy0ny", + "no_follow": False, + "num_comments": 849, + "num_crossposts": 24, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hqy0ny/bunnies_flop_over_when_they_feel_completely_safe/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "eMi5JzdWDMeDALsqK8bVceX3jbXTWS_S1D-Ie1hQxnc", + "resolutions": [ + { + "height": 60, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=108&crop=smart&format=pjpg&auto=webp&s=5c6d61e0d4934df3c1f4b7a4c3c3afdd4c31c037", + "width": 108, + }, + { + "height": 121, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=216&crop=smart&format=pjpg&auto=webp&s=24586000b5821e23ce78f395c1f294bbe3fa3945", + "width": 216, + }, + { + "height": 180, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=320&crop=smart&format=pjpg&auto=webp&s=dcaed0109703cbddd4914e138afdb61086cffd81", + "width": 320, + }, + { + "height": 360, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=640&crop=smart&format=pjpg&auto=webp&s=ef4f6dc33fe582b93e954114e9eb1447bbbc197b", + "width": 640, + }, + ], + "source": { + "height": 360, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?format=pjpg&auto=webp&s=b6e8cba9d25c684ecb7104c1e1c454dba7fd3f2f", + "width": 640, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 112661, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/asj4p03rdsa51/DASHPlaylist.mpd?a=1597351258%2CY2VmYTAyMWNmZjIwZjQ4YTBmMDc5MTRjOTU0NjliZWU3MDE2YTU3NjJiYzQxZWRiODY4ZTc1YWI1NDY4MWIxNA%3D%3D&v=1&f=sd", + "duration": 30, + "fallback_url": "https://v.redd.it/asj4p03rdsa51/DASH_360.mp4?source=fallback", + "height": 360, + "hls_url": "https://v.redd.it/asj4p03rdsa51/HLSPlaylist.m3u8?a=1597351258%2CY2QxM2I4Njk5MmIyOTRiZTBhNDQ2MDg0ZTM2NTllYzBjODBlYjNiNDc1Mzg2ODIxNDk4MTAzMzYyNzlmNjI1NQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/asj4p03rdsa51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/l_4Yk7NC8hz2HM0D3Hv2dK_nZBjpL8FL3NPv9WkRo8k.jpg", + "thumbnail_height": 78, + "thumbnail_width": 140, + "title": "Bunnies flop over when they feel " + "completely safe beside their " + "protectors", + "top_awarded_type": None, + "total_awards_received": 12, + "treatment_tags": [], + "ups": 112661, + "upvote_ratio": 0.94, + "url": "https://v.redd.it/asj4p03rdsa51", + "url_overridden_by_dest": "https://v.redd.it/asj4p03rdsa51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +external_video_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_ot2b2", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Dog splashing in water", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 140, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hulh8k", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.94, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 1142, + "total_awards_received": 0, + "media_embed": { + "content": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "width": 400, + "scrolling": False, + "height": 400, + }, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": { + "oembed": { + "provider_url": "https://gfycat.com", + "description": 'Hi! We use cookies and similar technologies ("cookies"), including third-party cookies, on this website to help operate and improve your experience on our site, monitor our site performance, and for advertising purposes. By clicking "Accept Cookies" below, you are giving us consent to use cookies (except consent is not required for cookies necessary to run our site).', + "title": "97991217 286625482366728 7551185146460766208 n", + "author_name": "Gfycat", + "height": 400, + "width": 400, + "html": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "thumbnail_width": 250, + "version": "1.0", + "provider_name": "Gfycat", + "thumbnail_url": "https://thumbs.gfycat.com/ExcellentInfantileAmericanwigeon-size_restricted.gif", + "type": "video", + "thumbnail_height": 250, + }, + "type": "gfycat.com", + }, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": { + "content": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "width": 400, + "scrolling": False, + "media_domain_url": "https://www.redditmedia.com/mediaembed/hulh8k", + "height": 400, + }, + "link_flair_text": None, + "can_mod_post": False, + "score": 1142, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://b.thumbs.redditmedia.com/eR_Cu4w1l9PwaM14RTEpnKD20EaK5mMxUbyK8BBDo_M.jpg", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "rich:video", + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [], + "created": 1595281442, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "gfycat.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://gfycat.com/excellentinfantileamericanwigeon", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/rZXN_aGbww8NNlhGWB-5cjHPonSST4S7aS6uaZyb_W4.jpg?auto=webp&s=2a2d3a1e0a06742bf752c1c4e1582c2fa49793a3", + "width": 250, + "height": 250, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/rZXN_aGbww8NNlhGWB-5cjHPonSST4S7aS6uaZyb_W4.jpg?width=108&crop=smart&auto=webp&s=35f61b003416516f664682717876a94d186793ae", + "width": 108, + "height": 108, + }, + { + "url": "https://external-preview.redd.it/rZXN_aGbww8NNlhGWB-5cjHPonSST4S7aS6uaZyb_W4.jpg?width=216&crop=smart&auto=webp&s=842416c1b8f8fae758a7ba6eb98af93ee2404a8d", + "width": 216, + "height": 216, + }, + ], + "variants": {}, + "id": "IVorc9dV9K9nJhhSVFKST92dfGfmhgBQjw257DWmJcE", + } + ], + "reddit_video_preview": { + "fallback_url": "https://v.redd.it/syp9pkiu00c51/DASH_360.mp4", + "height": 400, + "width": 400, + "scrubber_media_url": "https://v.redd.it/syp9pkiu00c51/DASH_96.mp4", + "dash_url": "https://v.redd.it/syp9pkiu00c51/DASHPlaylist.mpd", + "duration": 21, + "hls_url": "https://v.redd.it/syp9pkiu00c51/HLSPlaylist.m3u8", + "is_gif": True, + "transcoding_status": "completed", + }, + "enabled": False, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hulh8k", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheRikari", + "discussion_type": None, + "num_comments": 21, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hujqxu", + "author_flair_text_color": None, + "permalink": "/r/aww/comments/hulh8k/dog_splashing_in_water/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://gfycat.com/excellentinfantileamericanwigeon", + "subreddit_subscribers": 25721914, + "created_utc": 1595252642, + "num_crossposts": 0, + "media": { + "oembed": { + "provider_url": "https://gfycat.com", + "description": 'Hi! We use cookies and similar technologies ("cookies"), including third-party cookies, on this website to help operate and improve your experience on our site, monitor our site performance, and for advertising purposes. By clicking "Accept Cookies" below, you are giving us consent to use cookies (except consent is not required for cookies necessary to run our site).', + "title": "97991217 286625482366728 7551185146460766208 n", + "author_name": "Gfycat", + "height": 400, + "width": 400, + "html": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "thumbnail_width": 250, + "version": "1.0", + "provider_name": "Gfycat", + "thumbnail_url": "https://thumbs.gfycat.com/ExcellentInfantileAmericanwigeon-size_restricted.gif", + "type": "video", + "thumbnail_height": 250, + }, + "type": "gfycat.com", + }, + "is_video": False, + }, + } + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +external_gifv_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_ygx0p1u", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "if i fits i sits", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 74, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_humdlf", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.97, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 7512, + "total_awards_received": 1, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 7512, + "approved_by": None, + "author_premium": True, + "thumbnail": "https://b.thumbs.redditmedia.com/QHK44nUFZup-hfFX2Z1dXhk-1lPEmROUCB3bBujvTck.jpg", + "edited": False, + "author_flair_css_class": "k", + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "link", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1595284712, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.imgur.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": "confidence", + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.imgur.com/grVh2AG.gifv", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": False, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?auto=webp&s=c4ba246318b3502b080d37fcbdb12e07221401a9", + "width": 638, + "height": 338, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=108&crop=smart&auto=webp&s=c9c340a60ba3da1af3f5d5c08f3ed618ebd567d4", + "width": 108, + "height": 57, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=216&crop=smart&auto=webp&s=d05c0415e3dc63d097264bfb1b35b09676bd24f6", + "width": 216, + "height": 114, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=320&crop=smart&auto=webp&s=5c236179ccfff29e9ba980f31d5a6a9905adbe86", + "width": 320, + "height": 169, + }, + ], + "variants": {}, + "id": "4Z8zF5e4sZJnX4vWH7pZkbqiDPMCuh2J4kNotV9AGSI", + } + ], + "reddit_video_preview": { + "fallback_url": "https://v.redd.it/zzctc8y2dzb51/DASH_240.mp4", + "height": 338, + "width": 638, + "scrubber_media_url": "https://v.redd.it/zzctc8y2dzb51/DASH_96.mp4", + "dash_url": "https://v.redd.it/zzctc8y2dzb51/DASHPlaylist.mpd", + "duration": 44, + "hls_url": "https://v.redd.it/zzctc8y2dzb51/HLSPlaylist.m3u8", + "is_gif": True, + "transcoding_status": "completed", + }, + "enabled": False, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": False, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "humdlf", + "is_robot_indexable": True, + "report_reasons": None, + "author": "jasontaken", + "discussion_type": None, + "num_comments": 67, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/aww/comments/humdlf/if_i_fits_i_sits/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.imgur.com/grVh2AG.gifv", + "subreddit_subscribers": 25723833, + "created_utc": 1595255912, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + } + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +unknown_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "kind": "t1", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_ygx0p1u", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "if i fits i sits", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 74, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_humdlf", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.97, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 7512, + "total_awards_received": 1, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 7512, + "approved_by": None, + "author_premium": True, + "thumbnail": "https://b.thumbs.redditmedia.com/QHK44nUFZup-hfFX2Z1dXhk-1lPEmROUCB3bBujvTck.jpg", + "edited": False, + "author_flair_css_class": "k", + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "link", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1595284712, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.imgur.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": "confidence", + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.imgur.com/grVh2AG.gifv", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": False, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?auto=webp&s=c4ba246318b3502b080d37fcbdb12e07221401a9", + "width": 638, + "height": 338, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=108&crop=smart&auto=webp&s=c9c340a60ba3da1af3f5d5c08f3ed618ebd567d4", + "width": 108, + "height": 57, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=216&crop=smart&auto=webp&s=d05c0415e3dc63d097264bfb1b35b09676bd24f6", + "width": 216, + "height": 114, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=320&crop=smart&auto=webp&s=5c236179ccfff29e9ba980f31d5a6a9905adbe86", + "width": 320, + "height": 169, + }, + ], + "variants": {}, + "id": "4Z8zF5e4sZJnX4vWH7pZkbqiDPMCuh2J4kNotV9AGSI", + } + ], + "reddit_video_preview": { + "fallback_url": "https://v.redd.it/zzctc8y2dzb51/DASH_240.mp4", + "height": 338, + "width": 638, + "scrubber_media_url": "https://v.redd.it/zzctc8y2dzb51/DASH_96.mp4", + "dash_url": "https://v.redd.it/zzctc8y2dzb51/DASHPlaylist.mpd", + "duration": 44, + "hls_url": "https://v.redd.it/zzctc8y2dzb51/HLSPlaylist.m3u8", + "is_gif": True, + "transcoding_status": "completed", + }, + "enabled": False, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": False, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "humdlf", + "is_robot_indexable": True, + "report_reasons": None, + "author": "jasontaken", + "discussion_type": None, + "num_comments": 67, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/aww/comments/humdlf/if_i_fits_i_sits/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.imgur.com/grVh2AG.gifv", + "subreddit_subscribers": 25723833, + "created_utc": 1595255912, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + } + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +nsfw_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 8, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": True, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 9, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 2, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 2, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} + +spoiler_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 8, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 9, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 2, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": True, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 2, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} + +seen_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": True, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 8, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 9, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 2, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 2, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} + +upvote_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": True, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 99, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 9, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 150, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 2, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} + +comment_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": True, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 99, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 150, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 150, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 80, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} + +downvote_mock = { + "kind": "Listing", + "data": { + "modhash": "rjewztai5w0ab64547311ae1fb1f9cf81cd18949bfb629cb7f", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": True, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 10, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.7, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 99, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": "<!-- SC_OFF --><div class='md'><p>Welcome to <a href='/r/linux'>r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href='/r/linux'>r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href='/r/linuxadmin'>r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href='/r/linuxquestions'>r/linuxquestions</a>, <a href='/r/linux4noobs'>r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->", + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 150, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 544037, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 40, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 150, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 80, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 544037, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmytic", + "before": None, + }, +} diff --git a/src/newsreader/news/collection/tests/reddit/builder/tests.py b/src/newsreader/news/collection/tests/reddit/builder/tests.py new file mode 100644 index 0000000..3434972 --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/builder/tests.py @@ -0,0 +1,457 @@ +from datetime import datetime +from unittest.mock import Mock + +from django.test import TestCase + +import pytz + +from newsreader.news.collection.reddit import RedditBuilder +from newsreader.news.collection.tests.factories import SubredditFactory +from newsreader.news.collection.tests.reddit.builder.mocks import * +from newsreader.news.core.models import Post +from newsreader.news.core.tests.factories import RedditPostFactory + + +class RedditBuilderTestCase(TestCase): + def setUp(self): + self.maxDiff = None + + def test_simple_mock(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(simple_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual( + ("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys() + ) + + post = posts["hm0qct"] + + self.assertEquals(post.rule, subreddit) + self.assertEquals( + post.title, + "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + ) + self.assertIn( + " This megathread is also to hear opinions from anyone just starting out" + " with Linux or those that have used Linux (GNU or otherwise) for a long", + post.body, + ) + + self.assertIn( + "

    For those looking for certifications please use this megathread to ask about how" + " to get certified whether it's for the business world or for your own satisfaction." + ' Be sure to check out r/linuxadmin for more discussion in the' + " SysAdmin world!

    ", + post.body, + ) + + self.assertEquals(post.author, "AutoModerator") + self.assertEquals( + post.url, + "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + ) + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22)) + ) + + def test_empty_data(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(empty_mock, mock_stream) as builder: + builder.build() + builder.save() + + self.assertEquals(Post.objects.count(), 0) + + def test_unknown_mock(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(unknown_mock, mock_stream) as builder: + builder.build() + builder.save() + + self.assertEquals(Post.objects.count(), 0) + + def test_html_sanitizing(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(unsanitized_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hnd7cy",), posts.keys()) + + post = posts["hnd7cy"] + + self.assertEquals(post.body, "
    ") + + def test_long_author_text_is_truncated(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(author_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hnd7cy",), posts.keys()) + + post = posts["hnd7cy"] + + self.assertEquals(post.author, "TheQuantumZeroTheQuantumZeroTheQuantumZ…") + + def test_long_title_text_is_truncated(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(title_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hnd7cy",), posts.keys()) + + post = posts["hnd7cy"] + + self.assertEquals( + post.title, + 'Board statement on the LibreOffice 7.0 RC "Personal EditionBoard statement on the LibreOffice 7.0 RC "Personal Edition" label" labelBoard statement on the LibreOffice 7.0 RC "PersBoard statement on t…', + ) + + def test_duplicate_in_response(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(duplicate_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 2) + self.assertCountEqual(("hm0qct", "hna75r"), posts.keys()) + + def test_duplicate_in_database(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + duplicate_post = RedditPostFactory( + remote_identifier="hm0qct", rule=subreddit, title="foo" + ) + + with builder(simple_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 5) + self.assertCountEqual( + ("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys() + ) + + def test_image_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(image_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hr64xh", "hr4bxo", "hr14y5", "hr2fv0"), posts.keys()) + + post = posts["hr64xh"] + + title = ( + "Ya’ll, I just can’t... this is my " + "son, Judah. My wife and I have no " + "idea how we created such a " + "beautiful child." + ) + url = "https://i.redd.it/cm2qybia1va51.jpg" + + self.assertEquals( + "https://www.reddit.com/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/", + post.url, + ) + self.assertEquals( + f"
    {title}
    ", post.body + ) + + def test_external_image_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(external_image_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hr41am", "huoldn"), posts.keys()) + + post = posts["hr41am"] + + url = "http://gfycat.com/thatalivedogwoodclubgall" + title = "Excited cows have a new brush!" + + self.assertEquals( + f"", + post.body, + ) + self.assertEquals( + "https://www.reddit.com/r/aww/comments/hr41am/excited_cows_have_a_new_brush/", + post.url, + ) + + post = posts["huoldn"] + + url = "https://i.imgur.com/usfMVUJ.jpg" + title = "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallas’s cat kittens" + + self.assertEquals( + f"
    {title}
    ", post.body + ) + self.assertEquals( + "https://www.reddit.com/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/", + post.url, + ) + + def test_video_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(video_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hr32jf", "hr1r00", "hqy0ny", "hr0uzh"), posts.keys()) + + post = posts["hr1r00"] + + url = "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback" + + self.assertEquals( + post.url, + "https://www.reddit.com/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/", + ) + self.assertEquals( + f"
    ", + post.body, + ) + + def test_external_video_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(external_video_mock, mock_stream) as builder: + builder.build() + builder.save() + + post = Post.objects.get() + + self.assertEquals(post.remote_identifier, "hulh8k") + + self.assertEquals( + post.url, + "https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/", + ) + + title = "Dog splashing in water" + url = "https://gfycat.com/excellentinfantileamericanwigeon" + + self.assertEquals( + f"", + post.body, + ) + + def test_external_gifv_video_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(external_gifv_mock, mock_stream) as builder: + builder.build() + builder.save() + + post = Post.objects.get() + + self.assertEquals(post.remote_identifier, "humdlf") + + self.assertEquals( + post.url, "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/" + ) + + self.assertEquals( + "
    ", + post.body, + ) + + def test_link_only_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(simple_mock, mock_stream) as builder: + builder.build() + builder.save() + + post = Post.objects.get(remote_identifier="hngsj8") + + title = "KeePassXC 2.6.0 released" + url = "https://keepassxc.org/blog/2020-07-07-2.6.0-released/" + + self.assertIn( + f"", + post.body, + ) + + self.assertEquals( + post.url, + "https://www.reddit.com/r/linux/comments/hngsj8/keepassxc_260_released/", + ) + + def test_skip_not_known_post_type(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + with builder(unknown_mock, mock_stream) as builder: + builder.build() + builder.save() + + self.assertEquals(Post.objects.count(), 0) + + def test_nsfw_not_allowed(self): + builder = RedditBuilder + + subreddit = SubredditFactory(reddit_allow_nfsw=False) + mock_stream = Mock(rule=subreddit) + + with builder(nsfw_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 1) + self.assertCountEqual(("hna75r",), posts.keys()) + + def test_spoiler_not_allowed(self): + builder = RedditBuilder + + subreddit = SubredditFactory(reddit_allow_spoiler=False) + mock_stream = Mock(rule=subreddit) + + with builder(spoiler_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 1) + self.assertCountEqual(("hm0qct",), posts.keys()) + + def test_already_seen_not_allowed(self): + builder = RedditBuilder + + subreddit = SubredditFactory(reddit_allow_viewed=False) + mock_stream = Mock(rule=subreddit) + + with builder(seen_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 1) + self.assertCountEqual(("hna75r",), posts.keys()) + + def test_upvote_minimum(self): + builder = RedditBuilder + + subreddit = SubredditFactory(reddit_upvotes_min=100) + mock_stream = Mock(rule=subreddit) + + with builder(upvote_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 1) + self.assertCountEqual(("hna75r",), posts.keys()) + + def test_comments_minimum(self): + builder = RedditBuilder + + subreddit = SubredditFactory(reddit_comments_min=100) + mock_stream = Mock(rule=subreddit) + + with builder(comment_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 1) + self.assertCountEqual(("hm0qct",), posts.keys()) + + def test_downvote_maximum(self): + builder = RedditBuilder + + subreddit = SubredditFactory(reddit_downvotes_max=20) + mock_stream = Mock(rule=subreddit) + + with builder(downvote_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertEquals(Post.objects.count(), 1) + self.assertCountEqual(("hm0qct",), posts.keys()) diff --git a/src/newsreader/news/collection/tests/reddit/client/__init__.py b/src/newsreader/news/collection/tests/reddit/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/reddit/client/mocks.py b/src/newsreader/news/collection/tests/reddit/client/mocks.py new file mode 100644 index 0000000..6a11409 --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/client/mocks.py @@ -0,0 +1,160 @@ +# Note that some response data is truncated + +simple_mock = { + "data": { + "after": "t3_hjywyf", + "before": None, + "children": [ + { + "data": { + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "AutoModerator", + "banned_at_utc": None, + "banned_by": None, + "category": None, + "content_categories": None, + "created": 1593605471.0, + "created_utc": 1593576671.0, + "discussion_type": None, + "distinguished": "moderator", + "domain": "self.linux", + "edited": False, + "hidden": False, + "id": "hj34ck", + "locked": False, + "name": "t3_hj34ck", + "permalink": "/r/linux/comments/hj34ck/weekly_questions_and_hardware_thread_july_01_2020/", + "pinned": False, + "selftext": "Welcome to r/linux! If you're " + "new to Linux or trying to get " + "started this thread is for you. " + "Get help here or as always, " + "check out r/linuxquestions or " + "r/linux4noobs\n" + "\n" + "This megathread is for all your " + "question needs. As we don't " + "allow questions on r/linux " + "outside of this megathread, " + "please consider using " + "r/linuxquestions or " + "r/linux4noobs for the best " + "solution to your problem.\n" + "\n" + "Ask your hardware requests here " + "too or try r/linuxhardware!", + "selftext_html": "<!-- SC_OFF " + "--><div " + 'class="md"><p>Welcome ' + "to <a " + 'href="/r/linux">r/linux</a>! ' + "If you&#39;re new to " + "Linux or trying to get " + "started this thread is for " + "you. Get help here or as " + "always, check out <a " + 'href="/r/linuxquestions">r/linuxquestions</a> ' + "or <a " + 'href="/r/linux4noobs">r/linux4noobs</a></p>\n' + "\n" + "<p>This megathread is " + "for all your question " + "needs. As we don&#39;t " + "allow questions on <a " + 'href="/r/linux">r/linux</a> ' + "outside of this megathread, " + "please consider using <a " + 'href="/r/linuxquestions">r/linuxquestions</a> ' + "or <a " + 'href="/r/linux4noobs">r/linux4noobs</a> ' + "for the best solution to " + "your problem.</p>\n" + "\n" + "<p>Ask your hardware " + "requests here too or try " + "<a " + 'href="/r/linuxhardware">r/linuxhardware</a>!</p>\n' + "</div><!-- SC_ON " + "-->", + "spoiler": False, + "stickied": True, + "subreddit": "linux", + "subreddit_id": "t5_2qh1a", + "subreddit_name_prefixed": "r/linux", + "title": "Weekly Questions and Hardware " "Thread - July 01, 2020", + "url": "https://www.reddit.com/r/linux/comments/hj34ck/weekly_questions_and_hardware_thread_july_01_2020/", + "visited": False, + }, + "kind": "t3", + }, + { + "data": { + "archived": False, + "author": "AutoModerator", + "banned_at_utc": None, + "banned_by": None, + "category": None, + "created": 1593824903.0, + "created_utc": 1593796103.0, + "discussion_type": None, + "domain": "self.linux", + "edited": False, + "hidden": False, + "id": "hkmu0t", + "name": "t3_hkmu0t", + "permalink": "/r/linux/comments/hkmu0t/weekend_fluff_linux_in_the_wild_thread_july_03/", + "pinned": False, + "saved": False, + "selftext": "Welcome to the weekend! This " + "stickied thread is for you to " + "post pictures of your ubuntu " + "2006 install disk, slackware " + "floppies, on-topic memes or " + "more.\n" + "\n" + "When it's not the weekend, be " + "sure to check out " + "r/WildLinuxAppears or " + "r/linuxmemes!", + "selftext_html": "<!-- SC_OFF " + "--><div " + 'class="md"><p>Welcome ' + "to the weekend! This " + "stickied thread is for you " + "to post pictures of your " + "ubuntu 2006 install disk, " + "slackware floppies, " + "on-topic memes or " + "more.</p>\n" + "\n" + "<p>When it&#39;s " + "not the weekend, be sure to " + "check out <a " + 'href="/r/WildLinuxAppears">r/WildLinuxAppears</a> ' + "or <a " + 'href="/r/linuxmemes">r/linuxmemes</a>!</p>\n' + "</div><!-- SC_ON " + "-->", + "spoiler": False, + "stickied": True, + "subreddit": "linux", + "subreddit_id": "t5_2qh1a", + "subreddit_name_prefixed": "r/linux", + "subreddit_subscribers": 542073, + "subreddit_type": "public", + "thumbnail": "", + "title": "Weekend Fluff / Linux in the Wild " + "Thread - July 03, 2020", + "url": "https://www.reddit.com/r/linux/comments/hkmu0t/weekend_fluff_linux_in_the_wild_thread_july_03/", + "visited": False, + }, + "kind": "t3", + }, + ], + "dist": 27, + "modhash": None, + }, + "kind": "Listing", +} diff --git a/src/newsreader/news/collection/tests/reddit/client/tests.py b/src/newsreader/news/collection/tests/reddit/client/tests.py new file mode 100644 index 0000000..4dcc10f --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/client/tests.py @@ -0,0 +1,164 @@ +from unittest.mock import Mock, patch +from uuid import uuid4 + +from django.test import TestCase +from django.utils.lorem_ipsum import words + +from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.exceptions import ( + StreamDeniedException, + StreamException, + StreamNotFoundException, + StreamParseException, + StreamTimeOutException, + StreamTooManyException, +) +from newsreader.news.collection.reddit import RedditClient +from newsreader.news.collection.tests.factories import SubredditFactory + +from .mocks import simple_mock + + +class RedditClientTestCase(TestCase): + def setUp(self): + self.maxDiff = None + + self.patched_read = patch("newsreader.news.collection.reddit.RedditStream.read") + self.mocked_read = self.patched_read.start() + + def tearDown(self): + patch.stopall() + + def test_client_retrieves_single_rules(self): + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + self.mocked_read.return_value = (simple_mock, mock_stream) + + with RedditClient([[subreddit]]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertEquals(data, simple_mock) + self.assertEquals(stream, mock_stream) + + self.mocked_read.assert_called_once_with() + + def test_client_catches_stream_exception(self): + subreddit = SubredditFactory() + + self.mocked_read.side_effect = StreamException(message="Stream exception") + + with RedditClient([[subreddit]]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertEquals(data, None) + self.assertEquals(stream, None) + self.assertEquals(stream.rule.error, "Stream exception") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called_once_with() + + def test_client_catches_stream_not_found_exception(self): + subreddit = SubredditFactory.create() + + self.mocked_read.side_effect = StreamNotFoundException( + message="Stream not found" + ) + + with RedditClient([[subreddit]]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertEquals(data, None) + self.assertEquals(stream, None) + self.assertEquals(stream.rule.error, "Stream not found") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called_once_with() + + @patch("newsreader.news.collection.reddit.RedditTokenTask") + def test_client_catches_stream_denied_exception(self, mocked_task): + user = UserFactory( + reddit_access_token=str(uuid4()), reddit_refresh_token=str(uuid4()) + ) + subreddit = SubredditFactory(user=user) + + self.mocked_read.side_effect = StreamDeniedException(message="Token expired") + + with RedditClient([(subreddit,)]) as client: + results = [(data, stream) for data, stream in client] + + self.mocked_read.assert_called_once_with() + mocked_task.delay.assert_called_once_with(user.pk) + + self.assertEquals(len(results), 0) + + user.refresh_from_db() + subreddit.refresh_from_db() + + self.assertEquals(user.reddit_access_token, None) + self.assertEquals(subreddit.succeeded, False) + self.assertEquals(subreddit.error, "Token expired") + + def test_client_catches_stream_timed_out_exception(self): + subreddit = SubredditFactory() + + self.mocked_read.side_effect = StreamTimeOutException( + message="Stream timed out" + ) + + with RedditClient([[subreddit]]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertEquals(data, None) + self.assertEquals(stream, None) + self.assertEquals(stream.rule.error, "Stream timed out") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called_once_with() + + def test_client_catches_stream_too_many_exception(self): + subreddit = SubredditFactory() + + self.mocked_read.side_effect = StreamTooManyException + + with RedditClient([[subreddit]]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertEquals(data, None) + self.assertEquals(stream, None) + self.assertEquals(stream.rule.error, "Too many requests") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called_once_with() + + def test_client_catches_stream_parse_exception(self): + subreddit = SubredditFactory() + + self.mocked_read.side_effect = StreamParseException( + message="Stream could not be parsed" + ) + + with RedditClient([[subreddit]]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertEquals(data, None) + self.assertEquals(stream, None) + self.assertEquals(stream.rule.error, "Stream could not be parsed") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called_once_with() + + def test_client_catches_long_exception_text(self): + subreddit = SubredditFactory() + mock_stream = Mock(rule=subreddit) + + self.mocked_read.side_effect = StreamParseException(message=words(1000)) + + with RedditClient([[subreddit]]) as client: + for data, stream in client: + self.assertEquals(data, None) + self.assertEquals(stream, None) + self.assertEquals(len(stream.rule.error), 1024) + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called_once_with() diff --git a/src/newsreader/news/collection/tests/reddit/collector/__init__.py b/src/newsreader/news/collection/tests/reddit/collector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/reddit/collector/mocks.py b/src/newsreader/news/collection/tests/reddit/collector/mocks.py new file mode 100644 index 0000000..37d40d8 --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/collector/mocks.py @@ -0,0 +1,1662 @@ +simple_mock_1 = { + "kind": "Listing", + "data": { + "modhash": "khwcr8tmp613f1b92d55150adb744983e7f6c37e87e30f6432", + "dist": 26, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "starcitizen", + "selftext": "Welcome to the Star Citizen question and answer thread. Feel free to ask any questions you have related to SC here!\r\n\r\n---\r\n\r\nUseful Links and Resources:\r\n\r\n[Star Citizen Wiki](https://starcitizen.tools) - *The biggest and best wiki resource dedicated to Star Citizen*\r\n\r\n[Star Citizen FAQ](https://starcitizen.tools/Frequently_Asked_Questions) - *Chances the answer you need is here.* \r\n\r\n[Discord Help Channel](https://discord.gg/0STCP5tSe7x9NBSq) - *Often times community members will be here to help you with issues.*\r\n\r\n[Referral Code Randomizer](http://gorefer.me/starcitizen) - *Use this when creating a new account to get 5000 extra UEC.*\r\n\r\n[Download Star Citizen](https://robertsspaceindustries.com/download) - *Get the latest version of Star Citizen here*\r\n\r\n[Current Game Features](https://robertsspaceindustries.com/feature-list) - *Click here to see what you can currently do in Star Citizen.*\r\n\r\n[Development Roadmap](https://robertsspaceindustries.com/roadmap/board/1-Star-Citizen) - *The current development status of up and coming Star Citizen features.*\r\n\r\n[Pledge FAQ](https://support.robertsspaceindustries.com/hc/en-us/articles/115013194987-Pledges-FAQs) - *Official FAQ regarding spending money on the game.*", + "author_fullname": "t2_otk50", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Star Citizen: Question and Answer Thread", + "link_flair_richtext": [{"e": "text", "t": "QUESTION"}], + "subreddit_name_prefixed": "r/starcitizen", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "QUESTION", + "downs": 0, + "thumbnail_height": None, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm6byg", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.9, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 21, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": None, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "QUESTION", + "can_mod_post": False, + "score": 21, + "approved_by": None, + "author_premium": False, + "thumbnail": "self", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "self", + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594065605, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.starcitizen", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to the Star Citizen question and answer thread. Feel free to ask any questions you have related to SC here!</p>\n\n<hr/>\n\n<p>Useful Links and Resources:</p>\n\n<p><a href="https://starcitizen.tools">Star Citizen Wiki</a> - <em>The biggest and best wiki resource dedicated to Star Citizen</em></p>\n\n<p><a href="https://starcitizen.tools/Frequently_Asked_Questions">Star Citizen FAQ</a> - <em>Chances the answer you need is here.</em> </p>\n\n<p><a href="https://discord.gg/0STCP5tSe7x9NBSq">Discord Help Channel</a> - <em>Often times community members will be here to help you with issues.</em></p>\n\n<p><a href="http://gorefer.me/starcitizen">Referral Code Randomizer</a> - <em>Use this when creating a new account to get 5000 extra UEC.</em></p>\n\n<p><a href="https://robertsspaceindustries.com/download">Download Star Citizen</a> - <em>Get the latest version of Star Citizen here</em></p>\n\n<p><a href="https://robertsspaceindustries.com/feature-list">Current Game Features</a> - <em>Click here to see what you can currently do in Star Citizen.</em></p>\n\n<p><a href="https://robertsspaceindustries.com/roadmap/board/1-Star-Citizen">Development Roadmap</a> - <em>The current development status of up and coming Star Citizen features.</em></p>\n\n<p><a href="https://support.robertsspaceindustries.com/hc/en-us/articles/115013194987-Pledges-FAQs">Pledge FAQ</a> - <em>Official FAQ regarding spending money on the game.</em></p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/5qLIX6ObbErxkDiTz24uP6TbZGNVNyy2SeXusPhzaKA.jpg?auto=webp&s=738b5270a81373916191470a1da34cdcc54d8511", + "width": 332, + "height": 360, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/5qLIX6ObbErxkDiTz24uP6TbZGNVNyy2SeXusPhzaKA.jpg?width=108&crop=smart&auto=webp&s=e2ee2a9dae15472663b52c8cb4e002fdbbb6378c", + "width": 108, + "height": 117, + }, + { + "url": "https://external-preview.redd.it/5qLIX6ObbErxkDiTz24uP6TbZGNVNyy2SeXusPhzaKA.jpg?width=216&crop=smart&auto=webp&s=3690c60a9b533d376f159f306c6667b47ff42102", + "width": 216, + "height": 234, + }, + { + "url": "https://external-preview.redd.it/5qLIX6ObbErxkDiTz24uP6TbZGNVNyy2SeXusPhzaKA.jpg?width=320&crop=smart&auto=webp&s=4dcb434a5071329ecbb9f3543e4d06442ab141df", + "width": 320, + "height": 346, + }, + ], + "variants": {}, + "id": "KTE3H6RnWCasOJCFtdmgmw51FMzxSqXz_SRD6W5Rdsc", + } + ], + "enabled": False, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2v94d", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm6byg", + "is_robot_indexable": True, + "report_reasons": None, + "author": "UEE_Central_Computer", + "discussion_type": None, + "num_comments": 380, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/starcitizen/comments/hm6byg/star_citizen_question_and_answer_thread/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/starcitizen/comments/hm6byg/star_citizen_question_and_answer_thread/", + "subreddit_subscribers": 213071, + "created_utc": 1594036805, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "starcitizen", + "selftext": "", + "author_fullname": "t2_6wgp9w28", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "5 random people in a train felt like such a rare and special thing 😁", + "link_flair_richtext": [{"e": "text", "t": "FLUFF"}], + "subreddit_name_prefixed": "r/starcitizen", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "fluff", + "downs": 0, + "thumbnail_height": 78, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hpkhgj", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.98, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 892, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": "a87724f8-c2b5-11e4-b7e0-22000b2103f6", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "FLUFF", + "can_mod_post": False, + "score": 892, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://b.thumbs.redditmedia.com/YlF6BTm-DfnrZBeukYiOyrP-Fkj2xUQtk_V8ZeUD93w.jpg", + "edited": False, + "author_flair_css_class": "aurora", + "author_flair_richtext": [ + {"e": "text", "t": "🌌2013Backer🎮vGameDev🌌"} + ], + "gildings": {}, + "post_hint": "image", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594540209, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/0jkge020fba51.png", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://preview.redd.it/0jkge020fba51.png?auto=webp&s=c3a2b8cb860f839638a364d49abca04fd4f42094", + "width": 2560, + "height": 1440, + }, + "resolutions": [ + { + "url": "https://preview.redd.it/0jkge020fba51.png?width=108&crop=smart&auto=webp&s=778a7f7d9b2e0d713161e84b32c467ebde6cbc17", + "width": 108, + "height": 60, + }, + { + "url": "https://preview.redd.it/0jkge020fba51.png?width=216&crop=smart&auto=webp&s=53afc50cc2dd6c72470e76a4c3ff8ef597f66e0d", + "width": 216, + "height": 121, + }, + { + "url": "https://preview.redd.it/0jkge020fba51.png?width=320&crop=smart&auto=webp&s=089f9ff42e429b5062c143695e695cbb4ea5b679", + "width": 320, + "height": 180, + }, + { + "url": "https://preview.redd.it/0jkge020fba51.png?width=640&crop=smart&auto=webp&s=045327ac6fd113630c0faef426d86efaf04f55e2", + "width": 640, + "height": 360, + }, + { + "url": "https://preview.redd.it/0jkge020fba51.png?width=960&crop=smart&auto=webp&s=efbdc9ddcda1207fafa20bb45e82fbe24ed37df8", + "width": 960, + "height": 540, + }, + { + "url": "https://preview.redd.it/0jkge020fba51.png?width=1080&crop=smart&auto=webp&s=1b94c9951c60a788357dfa0fe21dd983efdcf1e7", + "width": 1080, + "height": 607, + }, + ], + "variants": {}, + "id": "r-JjrJn0RtZLaxMk_d-TCfW80pWgJ-5kjMaje54J5_I", + } + ], + "enabled": True, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "db099dc4-3538-11e5-97ec-0e7f0fa558f9", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": "🌌2013Backer🎮vGameDev🌌", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2v94d", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#007373", + "id": "hpkhgj", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Y_DK_Y", + "discussion_type": None, + "num_comments": 39, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/starcitizen/comments/hpkhgj/5_random_people_in_a_train_felt_like_such_a_rare/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/0jkge020fba51.png", + "subreddit_subscribers": 213071, + "created_utc": 1594511409, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "starcitizen", + "selftext": "", + "author_fullname": "t2_4brylpu5", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Drake Interplanetary Smartkey thing that I made!", + "link_flair_richtext": [{"e": "text", "t": "ARTWORK"}], + "subreddit_name_prefixed": "r/starcitizen", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "artwork", + "downs": 0, + "thumbnail_height": 78, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hph00n", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.97, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 547, + "total_awards_received": 1, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": True, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "ARTWORK", + "can_mod_post": False, + "score": 547, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://b.thumbs.redditmedia.com/gr7RYEjNN5FNc42LxuizFW_ZxWtS3xbZj1QfhIa-2Hw.jpg", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "image", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594527804, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/b6h74eljeaa51.png", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://preview.redd.it/b6h74eljeaa51.png?auto=webp&s=fd286c2dcd98378c34fde6e245cf13c357716dca", + "width": 1920, + "height": 1080, + }, + "resolutions": [ + { + "url": "https://preview.redd.it/b6h74eljeaa51.png?width=108&crop=smart&auto=webp&s=3150c2a2643d178eba735cb0bc222b8b29f46c8c", + "width": 108, + "height": 60, + }, + { + "url": "https://preview.redd.it/b6h74eljeaa51.png?width=216&crop=smart&auto=webp&s=9120ce40ce7439ca4d3431da7782a8c6acd2eebf", + "width": 216, + "height": 121, + }, + { + "url": "https://preview.redd.it/b6h74eljeaa51.png?width=320&crop=smart&auto=webp&s=83cd5c93fe7a19e5643df38eec3aefee54912faf", + "width": 320, + "height": 180, + }, + { + "url": "https://preview.redd.it/b6h74eljeaa51.png?width=640&crop=smart&auto=webp&s=b3e280a4a7fbaf794692c01f4ff63af0b8559700", + "width": 640, + "height": 360, + }, + { + "url": "https://preview.redd.it/b6h74eljeaa51.png?width=960&crop=smart&auto=webp&s=8ebac203688ba0e42c7975f3d7688dab25fc065b", + "width": 960, + "height": 540, + }, + { + "url": "https://preview.redd.it/b6h74eljeaa51.png?width=1080&crop=smart&auto=webp&s=8350e0b4e004820ef9f30501397d49a2121186ec", + "width": 1080, + "height": 607, + }, + ], + "variants": {}, + "id": "B2HxXfFibxKUtHO9eBwT-Bt_VrE870XhC0R5OFA95rI", + } + ], + "enabled": True, + }, + "all_awardings": [ + { + "giver_coin_reward": 0, + "subreddit_id": None, + "is_new": False, + "days_of_drip_extension": 0, + "coin_price": 50, + "id": "award_02d9ab2c-162e-4c01-8438-317a016ed3d9", + "penny_donate": 0, + "award_sub_type": "GLOBAL", + "coin_reward": 0, + "icon_url": "https://i.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png", + "days_of_premium": 0, + "resized_icons": [ + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=16&height=16&auto=webp&s=92e96be1dbd278dc987fbd9acc1bd5078566f254", + "width": 16, + "height": 16, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=32&height=32&auto=webp&s=83e14655f2b162b295f7d2c7058b9ad94cf8b73c", + "width": 32, + "height": 32, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=48&height=48&auto=webp&s=83038a4d6181d3c8f5107dbca4ddb735ca6c2231", + "width": 48, + "height": 48, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=64&height=64&auto=webp&s=3c4e39a7664d799ff50f32e9a3f96c3109d2e266", + "width": 64, + "height": 64, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=128&height=128&auto=webp&s=390bf9706b8e1a6215716ebcf6363373f125c339", + "width": 128, + "height": 128, + }, + ], + "icon_width": 2048, + "static_icon_width": 2048, + "start_date": None, + "is_enabled": True, + "description": "I'm in this with you.", + "end_date": None, + "subreddit_coin_reward": 0, + "count": 1, + "static_icon_height": 2048, + "name": "Take My Energy", + "resized_static_icons": [ + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=16&height=16&auto=webp&s=92e96be1dbd278dc987fbd9acc1bd5078566f254", + "width": 16, + "height": 16, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=32&height=32&auto=webp&s=83e14655f2b162b295f7d2c7058b9ad94cf8b73c", + "width": 32, + "height": 32, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=48&height=48&auto=webp&s=83038a4d6181d3c8f5107dbca4ddb735ca6c2231", + "width": 48, + "height": 48, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=64&height=64&auto=webp&s=3c4e39a7664d799ff50f32e9a3f96c3109d2e266", + "width": 64, + "height": 64, + }, + { + "url": "https://preview.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png?width=128&height=128&auto=webp&s=390bf9706b8e1a6215716ebcf6363373f125c339", + "width": 128, + "height": 128, + }, + ], + "icon_format": "PNG", + "icon_height": 2048, + "penny_price": 0, + "award_type": "global", + "static_icon_url": "https://i.redd.it/award_images/t5_22cerq/898sygoknoo41_TakeMyEnergy.png", + } + ], + "awarders": [], + "media_only": False, + "link_flair_template_id": "e3bb68b2-3538-11e5-bf5a-0e09b4299f63", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2v94d", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#ff66ac", + "id": "hph00n", + "is_robot_indexable": True, + "report_reasons": None, + "author": "HannahB888", + "discussion_type": None, + "num_comments": 38, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/starcitizen/comments/hph00n/drake_interplanetary_smartkey_thing_that_i_made/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/b6h74eljeaa51.png", + "subreddit_subscribers": 213071, + "created_utc": 1594499004, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "starcitizen", + "selftext": "", + "author_fullname": "t2_exlc6", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "A Historical Moment for CIG", + "link_flair_richtext": [{"e": "text", "t": "FLUFF"}], + "subreddit_name_prefixed": "r/starcitizen", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "fluff", + "downs": 0, + "thumbnail_height": 37, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hp9mlw", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.98, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 1444, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "FLUFF", + "can_mod_post": False, + "score": 1444, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://b.thumbs.redditmedia.com/YYdiE2x8fsn0ckVJiGCnBzUIOa1DA03ALh3TJuVlZks.jpg", + "edited": False, + "author_flair_css_class": "carrack", + "author_flair_richtext": [{"e": "text", "t": "AHV Artemis"}], + "gildings": {}, + "post_hint": "image", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594501406, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/fdh2ujp388a51.png", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://preview.redd.it/fdh2ujp388a51.png?auto=webp&s=605044c2757c1b5ca9060d3ec448090396a2f0dd", + "width": 424, + "height": 114, + }, + "resolutions": [ + { + "url": "https://preview.redd.it/fdh2ujp388a51.png?width=108&crop=smart&auto=webp&s=9789c6b76d45e46645fe2454555bfbd042a39815", + "width": 108, + "height": 29, + }, + { + "url": "https://preview.redd.it/fdh2ujp388a51.png?width=216&crop=smart&auto=webp&s=3f419183835c883f10b1caab3a7ecbec4ebbf3ec", + "width": 216, + "height": 58, + }, + { + "url": "https://preview.redd.it/fdh2ujp388a51.png?width=320&crop=smart&auto=webp&s=695ff914462b5b9bc253ce26f4a51f5f22641148", + "width": 320, + "height": 86, + }, + ], + "variants": {}, + "id": "XWdU5CBWG0-5mOzBRF65OnvZzQm2Btd2ldGMeJ8u_gI", + } + ], + "enabled": True, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "db099dc4-3538-11e5-97ec-0e7f0fa558f9", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": "AHV Artemis", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2v94d", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#007373", + "id": "hp9mlw", + "is_robot_indexable": True, + "report_reasons": None, + "author": "sam00197", + "discussion_type": None, + "num_comments": 194, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/starcitizen/comments/hp9mlw/a_historical_moment_for_cig/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/fdh2ujp388a51.png", + "subreddit_subscribers": 213071, + "created_utc": 1594472606, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "starcitizen", + "selftext": "", + "author_fullname": "t2_4dgjlpn7", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "This view. What's your favorite moon?", + "link_flair_richtext": [{"e": "text", "t": "DISCUSSION"}], + "subreddit_name_prefixed": "r/starcitizen", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "discussion", + "downs": 0, + "thumbnail_height": 78, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hpjn8x", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.96, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 182, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "DISCUSSION", + "can_mod_post": False, + "score": 182, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://a.thumbs.redditmedia.com/tKHL_2fn4Zo9FhrtP3UiJlQA7xkMU7-iN0ntJbhfa80.jpg", + "edited": False, + "author_flair_css_class": "", + "author_flair_richtext": [{"e": "text", "t": "new user/low karma"}], + "gildings": {}, + "post_hint": "image", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594537150, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/ovly7f9g6ba51.jpg", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://preview.redd.it/ovly7f9g6ba51.jpg?auto=webp&s=d7051e4c713e39c642c583e5e8ada57c9660fa26", + "width": 2560, + "height": 1440, + }, + "resolutions": [ + { + "url": "https://preview.redd.it/ovly7f9g6ba51.jpg?width=108&crop=smart&auto=webp&s=35f6ebe4531c12bc24532f01741bcf8100d954b2", + "width": 108, + "height": 60, + }, + { + "url": "https://preview.redd.it/ovly7f9g6ba51.jpg?width=216&crop=smart&auto=webp&s=a939922e34cf4ff6a82eeb22e71acb816ccc6d7b", + "width": 216, + "height": 121, + }, + { + "url": "https://preview.redd.it/ovly7f9g6ba51.jpg?width=320&crop=smart&auto=webp&s=9796767ed73e04a774d2f1ba8cf3662bbd4195eb", + "width": 320, + "height": 180, + }, + { + "url": "https://preview.redd.it/ovly7f9g6ba51.jpg?width=640&crop=smart&auto=webp&s=37fe4c262b752cb8dac903daf606be8f0ac3b44f", + "width": 640, + "height": 360, + }, + { + "url": "https://preview.redd.it/ovly7f9g6ba51.jpg?width=960&crop=smart&auto=webp&s=305245fd1d352634c86459131b11238fe09f5d2b", + "width": 960, + "height": 540, + }, + { + "url": "https://preview.redd.it/ovly7f9g6ba51.jpg?width=1080&crop=smart&auto=webp&s=e8438e4b666cf616646ffad09c153d120df1f1d9", + "width": 1080, + "height": 607, + }, + ], + "variants": {}, + "id": "SjRqA5h_B55WLnwAlocF6wcxIHZLgGBMpmb5nV1EQ4E", + } + ], + "enabled": True, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "ca858044-1916-11e2-a9b9-12313d168e98", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": "new user/low karma", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2v94d", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#014980", + "id": "hpjn8x", + "is_robot_indexable": True, + "report_reasons": None, + "author": "clericanubis", + "discussion_type": None, + "num_comments": 27, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/starcitizen/comments/hpjn8x/this_view_whats_your_favorite_moon/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/ovly7f9g6ba51.jpg", + "subreddit_subscribers": 213071, + "created_utc": 1594508350, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hplinp", + "before": None, + }, +} + +simple_mock_2 = { + "kind": "Listing", + "data": { + "modhash": "y4he8gfzh9f892e2bf3094bc06daba2e02288e617fecf555b5", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "Python", + "selftext": "Top Level comments must be **Job Opportunities.**\n\nPlease include **Location** or any other **Requirements** in your comment. If you require people to work on site in San Francisco, *you must note that in your post.* If you require an Engineering degree, *you must note that in your post*.\n\nPlease include as much information as possible.\n\nIf you are looking for jobs, send a PM to the poster.", + "author_fullname": "t2_628u", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "/r/Python Job Board for May, June, July", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/Python", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_gdfaip", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.98, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 108, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 108, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": "", + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1588640187, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.Python", + "allow_live_comments": True, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Top Level comments must be <strong>Job Opportunities.</strong></p>\n\n<p>Please include <strong>Location</strong> or any other <strong>Requirements</strong> in your comment. If you require people to work on site in San Francisco, <em>you must note that in your post.</em> If you require an Engineering degree, <em>you must note that in your post</em>.</p>\n\n<p>Please include as much information as possible.</p>\n\n<p>If you are looking for jobs, send a PM to the poster.</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": "reticulated", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh0y", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "gdfaip", + "is_robot_indexable": True, + "report_reasons": None, + "author": "aphoenix", + "discussion_type": None, + "num_comments": 38, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/Python/comments/gdfaip/rpython_job_board_for_may_june_july/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/Python/comments/gdfaip/rpython_job_board_for_may_june_july/", + "subreddit_subscribers": 616297, + "created_utc": 1588611387, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "Python", + "selftext": "# EDIT: AMA complete. Huge thanks to the PyCharm Team for holding this!\n\nAs mentioned in the comments you can use code `reddit20202` at [https://www.jetbrains.com/store/redeem/](https://www.jetbrains.com/store/redeem/) to try out PyCharm Professional as a new JetBrains customer!\n\nWe will be joined by members of the PyCharm Developer team from JetBrains to answer all sorts of questions on the PyCharm IDE and the Python language!\n\n[PyCharm](https://www.jetbrains.com/pycharm/) is the professional IDE for Python Developers with over 33% of respondents from the [2019 Python Developers Survey](https://www.jetbrains.com/lp/python-developers-survey-2019/) choosing it as their main editor.\n\nPyCharm features smart autocompletion, on-the-fly error checking and quick fixes as well as PEP8 compliance detection and automatic refactoring.\n\nIf you haven't checked out PyCharm then you definitely should, the Community Edition of PyCharm includes many key features such as the debugger, test runners, intelligent code completion and more!\n\nIf you are looking for a professional IDE for Python then the PyCharm Professional edition adds features such as advanced web development tools and database/SQL support, if you are a student or maintain an open source project make sure to take a look at the generous discounts JetBrains offer for their products!\n\nThe AMA will begin at 16:00 UTC on the 9th of July. Feel free to drop questions below for the PyCharm team to answer!\n\nWe will be joined by:\n\n* Nafiul Islam, u/nafiulislamjb (Developer Advocate for PyCharm)\n* Andrey Vlasovskikh, u/vlasovskikh (PyCharm Team Lead)", + "user_reports": [], + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "AMA with PyCharm team from JetBrains on 9th July @ 16:00 UTC", + "event_start": 1594310400, + "subreddit_name_prefixed": "r/Python", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "editors", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hmd2ez", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.94, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 60, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "author_fullname": "t2_145f96", + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Editors / IDEs", + "can_mod_post": False, + "score": 60, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": 1594321779, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594088635, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.Python", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><h1>EDIT: AMA complete. Huge thanks to the PyCharm Team for holding this!</h1>\n\n<p>As mentioned in the comments you can use code <code>reddit20202</code> at <a href="https://www.jetbrains.com/store/redeem/">https://www.jetbrains.com/store/redeem/</a> to try out PyCharm Professional as a new JetBrains customer!</p>\n\n<p>We will be joined by members of the PyCharm Developer team from JetBrains to answer all sorts of questions on the PyCharm IDE and the Python language!</p>\n\n<p><a href="https://www.jetbrains.com/pycharm/">PyCharm</a> is the professional IDE for Python Developers with over 33% of respondents from the <a href="https://www.jetbrains.com/lp/python-developers-survey-2019/">2019 Python Developers Survey</a> choosing it as their main editor.</p>\n\n<p>PyCharm features smart autocompletion, on-the-fly error checking and quick fixes as well as PEP8 compliance detection and automatic refactoring.</p>\n\n<p>If you haven&#39;t checked out PyCharm then you definitely should, the Community Edition of PyCharm includes many key features such as the debugger, test runners, intelligent code completion and more!</p>\n\n<p>If you are looking for a professional IDE for Python then the PyCharm Professional edition adds features such as advanced web development tools and database/SQL support, if you are a student or maintain an open source project make sure to take a look at the generous discounts JetBrains offer for their products!</p>\n\n<p>The AMA will begin at 16:00 UTC on the 9th of July. Feel free to drop questions below for the PyCharm team to answer!</p>\n\n<p>We will be joined by:</p>\n\n<ul>\n<li>Nafiul Islam, <a href="/u/nafiulislamjb">u/nafiulislamjb</a> (Developer Advocate for PyCharm)</li>\n<li>Andrey Vlasovskikh, <a href="/u/vlasovskikh">u/vlasovskikh</a> (PyCharm Team Lead)</li>\n</ul>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "confidence", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "49f2747c-4114-11ea-b9fe-0e741fe75651", + "link_flair_richtext": [], + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": "Owner of Python Discord", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh0y", + "event_end": 1594324800, + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "event_is_live": False, + "id": "hmd2ez", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Im__Joseph", + "discussion_type": None, + "num_comments": 65, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/Python/comments/hmd2ez/ama_with_pycharm_team_from_jetbrains_on_9th_july/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/Python/comments/hmd2ez/ama_with_pycharm_team_from_jetbrains_on_9th_july/", + "subreddit_subscribers": 616297, + "created_utc": 1594059835, + "num_crossposts": 2, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "Python", + "selftext": "", + "author_fullname": "t2_woll6", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "I am a medical student, and I recently programmed an open-source eye-tracker for brain research", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/Python", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "made-this", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hpr28u", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.99, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 439, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "4cc838b8-3159-11e1-83e4-12313d18ad57", + "is_original_content": False, + "user_reports": [], + "secure_media": { + "reddit_video": { + "fallback_url": "https://v.redd.it/tqzx750wzda51/DASH_360.mp4?source=fallback", + "height": 384, + "width": 512, + "scrubber_media_url": "https://v.redd.it/tqzx750wzda51/DASH_96.mp4", + "dash_url": "https://v.redd.it/tqzx750wzda51/DASHPlaylist.mpd?a=1597142191%2CY2JkNmU5Y2FmZGM1NzA5MjhkYTk5NjdmMWRmNWI4M2I2N2Q2MjA5NmIzZWRmODJiMjk0MzY4OTZlYTBiZmZlZg%3D%3D&v=1&f=sd", + "duration": 31, + "hls_url": "https://v.redd.it/tqzx750wzda51/HLSPlaylist.m3u8?a=1597142191%2CZDVhNWNjMGQ0OTBjOTU0Zjk5MDgwZmE2YzA1MGY5YzNlZThmZTAxZTgxODIxMGFjZDdlYzczOWFlYTcyMmMzNg%3D%3D&v=1&f=sd", + "is_gif": False, + "transcoding_status": "completed", + } + }, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "I Made This", + "can_mod_post": False, + "score": 439, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594571350, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "v.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://v.redd.it/tqzx750wzda51", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "d7dfae22-4113-11ea-b9fe-0e741fe75651", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": "Neuroscientist", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh0y", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hpr28u", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Sebaron", + "discussion_type": None, + "num_comments": 33, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/Python/comments/hpr28u/i_am_a_medical_student_and_i_recently_programmed/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://v.redd.it/tqzx750wzda51", + "subreddit_subscribers": 616297, + "created_utc": 1594542550, + "num_crossposts": 0, + "media": { + "reddit_video": { + "fallback_url": "https://v.redd.it/tqzx750wzda51/DASH_360.mp4?source=fallback", + "height": 384, + "width": 512, + "scrubber_media_url": "https://v.redd.it/tqzx750wzda51/DASH_96.mp4", + "dash_url": "https://v.redd.it/tqzx750wzda51/DASHPlaylist.mpd?a=1597142191%2CY2JkNmU5Y2FmZGM1NzA5MjhkYTk5NjdmMWRmNWI4M2I2N2Q2MjA5NmIzZWRmODJiMjk0MzY4OTZlYTBiZmZlZg%3D%3D&v=1&f=sd", + "duration": 31, + "hls_url": "https://v.redd.it/tqzx750wzda51/HLSPlaylist.m3u8?a=1597142191%2CZDVhNWNjMGQ0OTBjOTU0Zjk5MDgwZmE2YzA1MGY5YzNlZThmZTAxZTgxODIxMGFjZDdlYzczOWFlYTcyMmMzNg%3D%3D&v=1&f=sd", + "is_gif": False, + "transcoding_status": "completed", + } + }, + "is_video": True, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "Python", + "selftext": "", + "author_fullname": "t2_6zgzj94n", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "I made a filename simplifier which removes unnecessary tags, metadata, dashes, dots, underscores, and non-English characters from filenames (and folders) to give your library a neat look.", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/Python", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "made-this", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hpps6f", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.95, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 258, + "total_awards_received": 1, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": { + "reddit_video": { + "fallback_url": "https://v.redd.it/jq229anzada51/DASH_1080.mp4?source=fallback", + "height": 1080, + "width": 1920, + "scrubber_media_url": "https://v.redd.it/jq229anzada51/DASH_96.mp4", + "dash_url": "https://v.redd.it/jq229anzada51/DASHPlaylist.mpd?a=1597142191%2CZDU4Y2FmYzI2NjMzZTMxNzJkOThiMzJmYzBlOTMyMmEwNTg3MTFhMmU0OWZjZDljZGQ4MjAwMTgxMGVhYzU1OQ%3D%3D&v=1&f=sd", + "duration": 27, + "hls_url": "https://v.redd.it/jq229anzada51/HLSPlaylist.m3u8?a=1597142191%2CYmY1Y2Q5ZjQ0ZWVmODAxODQ3MGU3YzA1YzIxOTEzODFlNWQyMjE4MzAyYzNiMDM5NTI0N2M5OTRmY2YwN2NlOA%3D%3D&v=1&f=sd", + "is_gif": False, + "transcoding_status": "completed", + } + }, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "I Made This", + "can_mod_post": False, + "score": 258, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594563987, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "v.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://v.redd.it/jq229anzada51", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [ + { + "giver_coin_reward": 0, + "subreddit_id": None, + "is_new": False, + "days_of_drip_extension": 0, + "coin_price": 75, + "id": "award_9663243a-e77f-44cf-abc6-850ead2cd18d", + "penny_donate": 0, + "award_sub_type": "PREMIUM", + "coin_reward": 0, + "icon_url": "https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_512.png", + "days_of_premium": 0, + "resized_icons": [ + { + "url": "https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_16.png", + "width": 16, + "height": 16, + }, + { + "url": "https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_32.png", + "width": 32, + "height": 32, + }, + { + "url": "https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_48.png", + "width": 48, + "height": 48, + }, + { + "url": "https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_64.png", + "width": 64, + "height": 64, + }, + { + "url": "https://www.redditstatic.com/gold/awards/icon/SnooClappingPremium_128.png", + "width": 128, + "height": 128, + }, + ], + "icon_width": 512, + "static_icon_width": 512, + "start_date": None, + "is_enabled": True, + "description": "For an especially amazing showing.", + "end_date": None, + "subreddit_coin_reward": 0, + "count": 1, + "static_icon_height": 512, + "name": "Bravo Grande!", + "resized_static_icons": [ + { + "url": "https://preview.redd.it/award_images/t5_q0gj4/59e02tmkl4451_BravoGrande-Static.png?width=16&height=16&auto=webp&s=3459bdf1d1777821a831c5bf9834f4365263fcff", + "width": 16, + "height": 16, + }, + { + "url": "https://preview.redd.it/award_images/t5_q0gj4/59e02tmkl4451_BravoGrande-Static.png?width=32&height=32&auto=webp&s=9181d68065ccfccf2b1074e499cd7c1103aa2ce8", + "width": 32, + "height": 32, + }, + { + "url": "https://preview.redd.it/award_images/t5_q0gj4/59e02tmkl4451_BravoGrande-Static.png?width=48&height=48&auto=webp&s=339b368d395219120abc50d54fb3e2cdcad8ca4f", + "width": 48, + "height": 48, + }, + { + "url": "https://preview.redd.it/award_images/t5_q0gj4/59e02tmkl4451_BravoGrande-Static.png?width=64&height=64&auto=webp&s=de4ebbe92f9019de05aaa77f88810d44adbe1e50", + "width": 64, + "height": 64, + }, + { + "url": "https://preview.redd.it/award_images/t5_q0gj4/59e02tmkl4451_BravoGrande-Static.png?width=128&height=128&auto=webp&s=ba6c1add5204ea43e5af010bd9622392a42140e3", + "width": 128, + "height": 128, + }, + ], + "icon_format": "APNG", + "icon_height": 512, + "penny_price": 0, + "award_type": "global", + "static_icon_url": "https://i.redd.it/award_images/t5_q0gj4/59e02tmkl4451_BravoGrande-Static.png", + } + ], + "awarders": [], + "media_only": False, + "link_flair_template_id": "d7dfae22-4113-11ea-b9fe-0e741fe75651", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh0y", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hpps6f", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Hobo-TheGodOfPoverty", + "discussion_type": None, + "num_comments": 25, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/Python/comments/hpps6f/i_made_a_filename_simplifier_which_removes/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://v.redd.it/jq229anzada51", + "subreddit_subscribers": 616297, + "created_utc": 1594535187, + "num_crossposts": 0, + "media": { + "reddit_video": { + "fallback_url": "https://v.redd.it/jq229anzada51/DASH_1080.mp4?source=fallback", + "height": 1080, + "width": 1920, + "scrubber_media_url": "https://v.redd.it/jq229anzada51/DASH_96.mp4", + "dash_url": "https://v.redd.it/jq229anzada51/DASHPlaylist.mpd?a=1597142191%2CZDU4Y2FmYzI2NjMzZTMxNzJkOThiMzJmYzBlOTMyMmEwNTg3MTFhMmU0OWZjZDljZGQ4MjAwMTgxMGVhYzU1OQ%3D%3D&v=1&f=sd", + "duration": 27, + "hls_url": "https://v.redd.it/jq229anzada51/HLSPlaylist.m3u8?a=1597142191%2CYmY1Y2Q5ZjQ0ZWVmODAxODQ3MGU3YzA1YzIxOTEzODFlNWQyMjE4MzAyYzNiMDM5NTI0N2M5OTRmY2YwN2NlOA%3D%3D&v=1&f=sd", + "is_gif": False, + "transcoding_status": "completed", + } + }, + "is_video": True, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "Python", + "selftext": "", + "author_fullname": "t2_1kjpn251", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Concept Art: what might python look like in Japanese, without any English characters?", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/Python", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "discussion", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hp7uqe", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.94, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 1697, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Discussion", + "can_mod_post": False, + "score": 1697, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "ProgrammingLanguages", + "selftext": "", + "author_fullname": "t2_f4rdtgk", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Concept Art: what might python look like in Japanese, without any English characters?", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/ProgrammingLanguages", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_g9iu8x", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.96, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 440, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Discussion", + "can_mod_post": False, + "score": 440, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1588088407, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/ulc23n21jiv41.png", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "93811e06-0da7-11e8-a9a2-0e1129ea8e52", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qi8m", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "g9iu8x", + "is_robot_indexable": True, + "report_reasons": None, + "author": "MartialArtTetherball", + "discussion_type": None, + "num_comments": 65, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/ProgrammingLanguages/comments/g9iu8x/concept_art_what_might_python_look_like_in/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/ulc23n21jiv41.png", + "subreddit_subscribers": 43859, + "created_utc": 1588059607, + "num_crossposts": 2, + "media": None, + "is_video": False, + } + ], + "created": 1594492194, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/ulc23n21jiv41.png", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "0df42996-1c5e-11ea-b1a0-0e44e1c5b731", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh0y", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hp7uqe", + "is_robot_indexable": True, + "report_reasons": None, + "author": "SubstantialRange", + "discussion_type": None, + "num_comments": 182, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_g9iu8x", + "author_flair_text_color": None, + "permalink": "/r/Python/comments/hp7uqe/concept_art_what_might_python_look_like_in/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/ulc23n21jiv41.png", + "subreddit_subscribers": 616297, + "created_utc": 1594463394, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hozdzo", + "before": None, + }, +} + +empty_mock = { + "kind": "Listing", + "data": { + "modhash": "y4he8gfzh9f892e2bf3094bc06daba2e02288e617fecf555b5", + "dist": 27, + "children": [], + "after": "t3_hozdzo", + "before": None, + }, +} diff --git a/src/newsreader/news/collection/tests/reddit/collector/tests.py b/src/newsreader/news/collection/tests/reddit/collector/tests.py new file mode 100644 index 0000000..fa2f5d4 --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/collector/tests.py @@ -0,0 +1,204 @@ +from datetime import datetime +from unittest.mock import patch +from uuid import uuid4 + +from django.test import TestCase +from django.utils import timezone + +import pytz + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.exceptions import ( + StreamDeniedException, + StreamForbiddenException, + StreamNotFoundException, + StreamTimeOutException, +) +from newsreader.news.collection.reddit import RedditCollector +from newsreader.news.collection.tests.factories import SubredditFactory +from newsreader.news.collection.tests.reddit.collector.mocks import ( + empty_mock, + simple_mock_1, + simple_mock_2, +) +from newsreader.news.core.models import Post + + +class RedditCollectorTestCase(TestCase): + def setUp(self): + self.maxDiff = None + + self.patched_get = patch("newsreader.news.collection.reddit.fetch") + self.mocked_fetch = self.patched_get.start() + + self.patched_parse = patch( + "newsreader.news.collection.reddit.RedditStream.parse" + ) + self.mocked_parse = self.patched_parse.start() + + def tearDown(self): + patch.stopall() + + def test_simple_batch(self): + self.mocked_parse.side_effect = (simple_mock_1, simple_mock_2) + + rules = ( + (subreddit,) + for subreddit in SubredditFactory.create_batch( + user__reddit_access_token=str(uuid4()), + user__reddit_refresh_token=str(uuid4()), + enabled=True, + size=2, + ) + ) + + collector = RedditCollector() + collector.collect(rules=rules) + + self.assertCountEqual( + Post.objects.values_list("remote_identifier", flat=True), + ( + "hm6byg", + "hpkhgj", + "hph00n", + "hp9mlw", + "hpjn8x", + "gdfaip", + "hmd2ez", + "hpr28u", + "hpps6f", + "hp7uqe", + ), + ) + + for subreddit in rules: + with self.subTest(subreddit=subreddit): + self.assertEquals(subreddit.succeeded, True) + self.assertEquals(subreddit.last_run, timezone.now()) + self.assertEquals(subreddit.error, None) + + post = Post.objects.get( + remote_identifier="hph00n", rule__type=RuleTypeChoices.subreddit + ) + + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 7, 11, 22, 23, 24)) + ) + + self.assertEquals(post.author, "HannahB888") + self.assertEquals( + post.title, "Drake Interplanetary Smartkey thing that I made!" + ) + self.assertEquals( + post.url, + "https://www.reddit.com/r/starcitizen/comments/hph00n/drake_interplanetary_smartkey_thing_that_i_made/", + ) + + post = Post.objects.get( + remote_identifier="hpr28u", rule__type=RuleTypeChoices.subreddit + ) + + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 7, 12, 10, 29, 10)) + ) + + self.assertEquals(post.author, "Sebaron") + self.assertEquals( + post.title, + "I am a medical student, and I recently programmed an open-source eye-tracker for brain research", + ) + self.assertEquals( + post.url, + "https://www.reddit.com/r/Python/comments/hpr28u/i_am_a_medical_student_and_i_recently_programmed/", + ) + + def test_empty_batch(self): + self.mocked_parse.side_effect = (empty_mock, empty_mock) + + rules = ( + (subreddit,) + for subreddit in SubredditFactory.create_batch( + user__reddit_access_token=str(uuid4()), + user__reddit_refresh_token=str(uuid4()), + enabled=True, + size=2, + ) + ) + + collector = RedditCollector() + collector.collect(rules=rules) + + self.assertEquals(Post.objects.count(), 0) + + for subreddit in rules: + with self.subTest(subreddit=subreddit): + self.assertEquals(subreddit.succeeded, True) + self.assertEquals(subreddit.last_run, timezone.now()) + self.assertEquals(subreddit.error, None) + + def test_not_found(self): + self.mocked_fetch.side_effect = StreamNotFoundException + + rule = SubredditFactory( + user__reddit_access_token=str(uuid4()), + user__reddit_refresh_token=str(uuid4()), + enabled=True, + ) + + collector = RedditCollector() + collector.collect(rules=((rule,),)) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream not found") + + @patch("newsreader.news.collection.reddit.RedditTokenTask") + def test_denied(self, mocked_task): + self.mocked_fetch.side_effect = StreamDeniedException + + rule = SubredditFactory( + user__reddit_access_token=str(uuid4()), + user__reddit_refresh_token=str(uuid4()), + enabled=True, + ) + + collector = RedditCollector() + collector.collect(rules=((rule,),)) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream does not have sufficient permissions") + + mocked_task.delay.assert_called_once_with(rule.user.pk) + + def test_forbidden(self): + self.mocked_fetch.side_effect = StreamForbiddenException + + rule = SubredditFactory( + user__reddit_access_token=str(uuid4()), + user__reddit_refresh_token=str(uuid4()), + enabled=True, + ) + + collector = RedditCollector() + collector.collect(rules=((rule,),)) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream forbidden") + + def test_timed_out(self): + self.mocked_fetch.side_effect = StreamTimeOutException + + rule = SubredditFactory( + user__reddit_access_token=str(uuid4()), + user__reddit_refresh_token=str(uuid4()), + enabled=True, + ) + + collector = RedditCollector() + collector.collect(rules=((rule,),)) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(rule.succeeded, False) + self.assertEquals(rule.error, "Stream timed out") diff --git a/src/newsreader/news/collection/tests/reddit/stream/__init__.py b/src/newsreader/news/collection/tests/reddit/stream/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/reddit/stream/mocks.py b/src/newsreader/news/collection/tests/reddit/stream/mocks.py new file mode 100644 index 0000000..148b31a --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/stream/mocks.py @@ -0,0 +1,3289 @@ +simple_mock = { + "kind": "Listing", + "data": { + "modhash": "sgq4fdizx94db5c05b57f9957a4b8b2d5e24b712f5a507cffd", + "dist": 27, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.\n\nLet us know what's annoying you, whats making you happy, or something that you want to get out to r/linux but didn't make the cut into a full post of it's own.\n\nFor those looking for certifications please use this megathread to ask about how to get certified whether it's for the business world or for your own satisfaction. Be sure to check out r/linuxadmin for more discussion in the SysAdmin world!\n\n_Please keep questions in r/linuxquestions, r/linux4noobs, or the Wednesday automod thread._", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hm0qct", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.65, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 6, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 6, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594037482.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a> rants and experiences! This megathread is also to hear opinions from anyone just starting out with Linux or those that have used Linux (GNU or otherwise) for a long time.</p>\n\n<p>Let us know what&#39;s annoying you, whats making you happy, or something that you want to get out to <a href="/r/linux">r/linux</a> but didn&#39;t make the cut into a full post of it&#39;s own.</p>\n\n<p>For those looking for certifications please use this megathread to ask about how to get certified whether it&#39;s for the business world or for your own satisfaction. Be sure to check out <a href="/r/linuxadmin">r/linuxadmin</a> for more discussion in the SysAdmin world!</p>\n\n<p><em>Please keep questions in <a href="/r/linuxquestions">r/linuxquestions</a>, <a href="/r/linux4noobs">r/linux4noobs</a>, or the Wednesday automod thread.</em></p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hm0qct", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 8, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", + "subreddit_subscribers": 543995, + "created_utc": 1594008682.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Welcome to r/linux! If you're new to Linux or trying to get started this thread is for you. Get help here or as always, check out r/linuxquestions or r/linux4noobs\n\nThis megathread is for all your question needs. As we don't allow questions on r/linux outside of this megathread, please consider using r/linuxquestions or r/linux4noobs for the best solution to your problem.\n\nAsk your hardware requests here too or try r/linuxhardware!", + "author_fullname": "t2_6l4z3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Weekly Questions and Hardware Thread - July 08, 2020", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hna75r", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.5, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 0, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 0, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594210138.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Welcome to <a href="/r/linux">r/linux</a>! If you&#39;re new to Linux or trying to get started this thread is for you. Get help here or as always, check out <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a></p>\n\n<p>This megathread is for all your question needs. As we don&#39;t allow questions on <a href="/r/linux">r/linux</a> outside of this megathread, please consider using <a href="/r/linuxquestions">r/linuxquestions</a> or <a href="/r/linux4noobs">r/linux4noobs</a> for the best solution to your problem.</p>\n\n<p>Ask your hardware requests here too or try <a href="/r/linuxhardware">r/linuxhardware</a>!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": "new", + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": "moderator", + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hna75r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "AutoModerator", + "discussion_type": None, + "num_comments": 2, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "parent_whitelist_status": "all_ads", + "stickied": True, + "url": "https://www.reddit.com/r/linux/comments/hna75r/weekly_questions_and_hardware_thread_july_08_2020/", + "subreddit_subscribers": 543995, + "created_utc": 1594181338.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_gr7k5", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Here's a feature Linux could borrow from BSD: in-kernel debugger with built-in hangman game", + "link_flair_richtext": [{"e": "text", "t": "Fluff"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hngs71", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.9, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 135, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Fluff", + "can_mod_post": False, + "score": 135, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594242629.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/wmc8tp2ium951.jpg", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "af8918be-6777-11e7-8273-0e925d908786", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#9a2bff", + "id": "hngs71", + "is_robot_indexable": True, + "report_reasons": None, + "author": "the_humeister", + "discussion_type": None, + "num_comments": 20, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hngs71/heres_a_feature_linux_could_borrow_from_bsd/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/wmc8tp2ium951.jpg", + "subreddit_subscribers": 543995, + "created_utc": 1594213829.0, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_k9f35", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "KeePassXC 2.6.0 released", + "link_flair_richtext": [{"e": "text", "t": "Software Release"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hngsj8", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.97, + "author_flair_background_color": "transparent", + "subreddit_type": "public", + "ups": 126, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "fa602e36-cdf6-11e8-93c9-0e41ac35f4cc", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Software Release", + "can_mod_post": False, + "score": 126, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [ + { + "a": ":ubuntu:", + "e": "emoji", + "u": "https://emoji.redditmedia.com/uwmddx7qqpr11_t5_2qh1a/ubuntu", + } + ], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594242666.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "keepassxc.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://keepassxc.org/blog/2020-07-07-2.6.0-released/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "904ea3e4-6748-11e7-b925-0ef3dfbb807a", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": ":ubuntu:", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#349e48", + "id": "hngsj8", + "is_robot_indexable": True, + "report_reasons": None, + "author": "nixcraft", + "discussion_type": None, + "num_comments": 42, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/linux/comments/hngsj8/keepassxc_260_released/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://keepassxc.org/blog/2020-07-07-2.6.0-released/", + "subreddit_subscribers": 543995, + "created_utc": 1594213866.0, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [{"e": "text", "t": "Popular Application"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd7cy", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.95, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 223, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Popular Application", + "can_mod_post": False, + "score": 223, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "libreoffice", + "selftext": "", + "author_fullname": "t2_hlv0o", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": 'Board statement on the LibreOffice 7.0 RC "Personal Edition" label', + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/libreoffice", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnd6yo", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.94, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 28, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "News", + "can_mod_post": False, + "score": 28, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594224961.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "dc82ac98-bafb-11e4-9f88-22000b310327", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2s4nt", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnd6yo", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 38, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/libreoffice/comments/hnd6yo/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 4669, + "created_utc": 1594196161.0, + "num_crossposts": 2, + "media": None, + "is_video": False, + } + ], + "created": 1594225018.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.documentfoundation.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "7127ec98-5859-11e8-9488-0e8717893ec8", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#0aa18f", + "id": "hnd7cy", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheQuantumZero", + "discussion_type": None, + "num_comments": 109, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hnd6yo", + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnd7cy/board_statement_on_the_libreoffice_70_rc_personal/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.documentfoundation.org/blog/2020/07/06/board-statement-on-the-libreoffice-7-0rc-personal-edition-label/", + "subreddit_subscribers": 543995, + "created_utc": 1594196218.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_6cxnzaq2", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Gentoo Now on Android Platform !!!", + "link_flair_richtext": [{"e": "text", "t": "Mobile Linux"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnemei", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.87, + "author_flair_background_color": "transparent", + "subreddit_type": "public", + "ups": 78, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "a54a7460-cdf6-11e8-b31c-0e89679a2148", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Mobile Linux", + "can_mod_post": False, + "score": 78, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [ + { + "a": ":arch:", + "e": "emoji", + "u": "https://emoji.redditmedia.com/tip79drnqpr11_t5_2qh1a/arch", + } + ], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594232773.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "gentoo.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://www.gentoo.org/news/2020/07/07/gentoo-android.html", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "84162644-5859-11e8-b9ed-0efda312d094", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": ":arch:", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#d78216", + "id": "hnemei", + "is_robot_indexable": True, + "report_reasons": None, + "author": "draplon", + "discussion_type": None, + "num_comments": 21, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/linux/comments/hnemei/gentoo_now_on_android_platform/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.gentoo.org/news/2020/07/07/gentoo-android.html", + "subreddit_subscribers": 543995, + "created_utc": 1594203973.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_f9vxe", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Google is teaming up with Ubuntu to bring Flutter apps to Linux", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hniojf", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.77, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 31, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 31, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594249580.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "androidpolice.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://www.androidpolice.com/2020/07/08/google-is-teaming-up-with-ubuntu-to-bring-flutter-apps-to-linux/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hniojf", + "is_robot_indexable": True, + "report_reasons": None, + "author": "bilal4hmed", + "discussion_type": None, + "num_comments": 24, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hniojf/google_is_teaming_up_with_ubuntu_to_bring_flutter/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.androidpolice.com/2020/07/08/google-is-teaming-up-with-ubuntu-to-bring-flutter-apps-to-linux/", + "subreddit_subscribers": 543995, + "created_utc": 1594220780.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_k9f35", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Ariane RISC-V CPU \u2013 An open source CPU capable of booting Linux", + "link_flair_richtext": [{"e": "text", "t": "Hardware"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hngr1j", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.89, + "author_flair_background_color": "transparent", + "subreddit_type": "public", + "ups": 49, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "fa602e36-cdf6-11e8-93c9-0e41ac35f4cc", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Hardware", + "can_mod_post": False, + "score": 49, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [ + { + "a": ":ubuntu:", + "e": "emoji", + "u": "https://emoji.redditmedia.com/uwmddx7qqpr11_t5_2qh1a/ubuntu", + } + ], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594242511.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "github.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://github.com/openhwgroup/cva6", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "3d48793a-c823-11e8-9a58-0ee3c97eb952", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": ":ubuntu:", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#cc5289", + "id": "hngr1j", + "is_robot_indexable": True, + "report_reasons": None, + "author": "nixcraft", + "discussion_type": None, + "num_comments": 15, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/linux/comments/hngr1j/ariane_riscv_cpu_an_open_source_cpu_capable_of/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://github.com/openhwgroup/cva6", + "subreddit_subscribers": 543995, + "created_utc": 1594213711.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_6kt9ukjs", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Canonical enables Linux desktop app support with Flutter", + "link_flair_richtext": [{"e": "text", "t": "Software Release"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnj1ap", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.79, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 24, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Software Release", + "can_mod_post": False, + "score": 24, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594250752.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "ubuntu.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://ubuntu.com/blog/canonical-enables-linux-desktop-app-support-with-flutter", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "904ea3e4-6748-11e7-b925-0ef3dfbb807a", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#349e48", + "id": "hnj1ap", + "is_robot_indexable": True, + "report_reasons": None, + "author": "hmblhstl", + "discussion_type": None, + "num_comments": 28, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnj1ap/canonical_enables_linux_desktop_app_support_with/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://ubuntu.com/blog/canonical-enables-linux-desktop-app-support-with-flutter", + "subreddit_subscribers": 543995, + "created_utc": 1594221952.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_3vf8x", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Sandboxing in Linux with zero lines of code", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnfzbm", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.83, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 30, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 30, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594239285.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "blog.cloudflare.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://blog.cloudflare.com/sandboxing-in-linux-with-zero-lines-of-code/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnfzbm", + "is_robot_indexable": True, + "report_reasons": None, + "author": "pimterry", + "discussion_type": None, + "num_comments": 0, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnfzbm/sandboxing_in_linux_with_zero_lines_of_code/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://blog.cloudflare.com/sandboxing-in-linux-with-zero-lines-of-code/", + "subreddit_subscribers": 543995, + "created_utc": 1594210485.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_318in", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "SUSE Enters Into Definitive Agreement to Acquire Rancher Labs", + "link_flair_richtext": [ + {"e": "text", "t": "Open Source Organization"} + ], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnh5ux", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.84, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 26, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Open Source Organization", + "can_mod_post": False, + "score": 26, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594244123.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "rancher.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://rancher.com/blog/2020/suse-to-acquire-rancher/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "8a1dd4b0-5859-11e8-a2c7-0e5ebdbe24d6", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#800000", + "id": "hnh5ux", + "is_robot_indexable": True, + "report_reasons": None, + "author": "hjames9", + "discussion_type": None, + "num_comments": 5, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnh5ux/suse_enters_into_definitive_agreement_to_acquire/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://rancher.com/blog/2020/suse-to-acquire-rancher/", + "subreddit_subscribers": 543995, + "created_utc": 1594215323.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_j1a5", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Mint drops Ubuntu Snap packages [LWN.net]", + "link_flair_richtext": [{"e": "text", "t": "Distro News"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": True, + "name": "t3_hnlt4l", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.8, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 9, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Distro News", + "can_mod_post": False, + "score": 9, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594259641.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "lwn.net", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://lwn.net/SubscriberLink/825005/6440c82feb745bbe/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "6888e772-5859-11e8-82ff-0e816ab71260", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#0dd3bb", + "id": "hnlt4l", + "is_robot_indexable": True, + "report_reasons": None, + "author": "tapo", + "discussion_type": None, + "num_comments": 3, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnlt4l/linux_mint_drops_ubuntu_snap_packages_lwnnet/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://lwn.net/SubscriberLink/825005/6440c82feb745bbe/", + "subreddit_subscribers": 543995, + "created_utc": 1594230841.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_4i3yk", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Announcing Flutter Linux Alpha with Canonical", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hniq04", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.6, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 6, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 6, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594249712.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "medium.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://medium.com/flutter/announcing-flutter-linux-alpha-with-canonical-19eb824590a9", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hniq04", + "is_robot_indexable": True, + "report_reasons": None, + "author": "popeydc", + "discussion_type": None, + "num_comments": 3, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hniq04/announcing_flutter_linux_alpha_with_canonical/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://medium.com/flutter/announcing-flutter-linux-alpha-with-canonical-19eb824590a9", + "subreddit_subscribers": 543995, + "created_utc": 1594220912.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_611c0ard", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "New anti-encryption bill worse than EARN IT, would force a backdoor into any US device/software. Act now to stop both.", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hmp66i", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.98, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 3340, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 3340, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594131589.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "tutanota.com", + "allow_live_comments": True, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://tutanota.com/blog/posts/lawful-access-encrypted-data-act-backdoor", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hmp66i", + "is_robot_indexable": True, + "report_reasons": None, + "author": "fossfans", + "discussion_type": None, + "num_comments": 380, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hmp66i/new_antiencryption_bill_worse_than_earn_it_would/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://tutanota.com/blog/posts/lawful-access-encrypted-data-act-backdoor", + "subreddit_subscribers": 543995, + "created_utc": 1594102789.0, + "num_crossposts": 2, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "We have had Freesync \"support\" for quite some time now, but it is extremely restrictive and very picky to get it working. Just the requirements to have Freesync working is no-go for many:\n\n\\-> Single monitor only;\n\n\\-> No video playback or turning it on while on desktop;\n\n\\-> Should only be turned on only while the game/software in question is in fullscreen;\n\n\\-> X11, no Wayland;\n\n\\-> Only tested/working distro is Ubuntu 16.04.3;\n\n\\-> Need of setting it up through some quite cryptic commands;\n\n\\-> Doesn't work after hotplug or system restart;\n\n\\-> No Freesync over HDMI (which isn't a massive problem, but a nice option to have);\n\n\\-> Apparently only OpenGL, no Vulkan (Steam Play/Proton, which is the main purpose for Freesync at the moment, doesn't work);\n\n&#x200B;\n\nI am not really complaining, because I do know that Freesync is hard to get working on Linux, but we have had so many advancements on the gaming side of Linux, and we are still stuck with all of these restrictions to use Freesync, which is quite a useful functionality for almost every gamer. If Mozilla got video decoding working well on Wayland, I hope (Idk anything about this, just hoping) that it could also be easy to implement Freesync on Wayland too.\n\nWe just haven't had that many improvements on this side of the Linux gaming world, and I'd like to know if it is lack of support/interest by AMD, or if it actually is extremely hard to implement it on Linux. Freesync would also be very useful for those who are running monitors over 60Hz, so that those 60FPS videos don't look as weird as they do while playing back on higher refresh rate monitors. It is just a nice thing for everybody, really!", + "author_fullname": "t2_1afv9v8g", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Any evolution on the Freesync situation on Linux?", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hn7agp", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.85, + "author_flair_background_color": "transparent", + "subreddit_type": "public", + "ups": 83, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "fa602e36-cdf6-11e8-93c9-0e41ac35f4cc", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 83, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [ + { + "a": ":ubuntu:", + "e": "emoji", + "u": "https://emoji.redditmedia.com/uwmddx7qqpr11_t5_2qh1a/ubuntu", + } + ], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594199056.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>We have had Freesync &quot;support&quot; for quite some time now, but it is extremely restrictive and very picky to get it working. Just the requirements to have Freesync working is no-go for many:</p>\n\n<p>-&gt; Single monitor only;</p>\n\n<p>-&gt; No video playback or turning it on while on desktop;</p>\n\n<p>-&gt; Should only be turned on only while the game/software in question is in fullscreen;</p>\n\n<p>-&gt; X11, no Wayland;</p>\n\n<p>-&gt; Only tested/working distro is Ubuntu 16.04.3;</p>\n\n<p>-&gt; Need of setting it up through some quite cryptic commands;</p>\n\n<p>-&gt; Doesn&#39;t work after hotplug or system restart;</p>\n\n<p>-&gt; No Freesync over HDMI (which isn&#39;t a massive problem, but a nice option to have);</p>\n\n<p>-&gt; Apparently only OpenGL, no Vulkan (Steam Play/Proton, which is the main purpose for Freesync at the moment, doesn&#39;t work);</p>\n\n<p>&#x200B;</p>\n\n<p>I am not really complaining, because I do know that Freesync is hard to get working on Linux, but we have had so many advancements on the gaming side of Linux, and we are still stuck with all of these restrictions to use Freesync, which is quite a useful functionality for almost every gamer. If Mozilla got video decoding working well on Wayland, I hope (Idk anything about this, just hoping) that it could also be easy to implement Freesync on Wayland too.</p>\n\n<p>We just haven&#39;t had that many improvements on this side of the Linux gaming world, and I&#39;d like to know if it is lack of support/interest by AMD, or if it actually is extremely hard to implement it on Linux. Freesync would also be very useful for those who are running monitors over 60Hz, so that those 60FPS videos don&#39;t look as weird as they do while playing back on higher refresh rate monitors. It is just a nice thing for everybody, really!</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": ":ubuntu:", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hn7agp", + "is_robot_indexable": True, + "report_reasons": None, + "author": "mreich98", + "discussion_type": None, + "num_comments": 36, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/linux/comments/hn7agp/any_evolution_on_the_freesync_situation_on_linux/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.reddit.com/r/linux/comments/hn7agp/any_evolution_on_the_freesync_situation_on_linux/", + "subreddit_subscribers": 543995, + "created_utc": 1594170256.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_7ccf", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Running Rosetta@home on a Raspberry Pi with Fedora IoT", + "link_flair_richtext": [{"e": "text", "t": "Popular Application"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnfw0h", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.73, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 8, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Popular Application", + "can_mod_post": False, + "score": 8, + "approved_by": None, + "author_premium": True, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594238884.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "fedoramagazine.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://fedoramagazine.org/running-rosettahome-on-a-raspberry-pi-with-fedora-iot/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "7127ec98-5859-11e8-9488-0e8717893ec8", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#0aa18f", + "id": "hnfw0h", + "is_robot_indexable": True, + "report_reasons": None, + "author": "speckz", + "discussion_type": None, + "num_comments": 1, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnfw0h/running_rosettahome_on_a_raspberry_pi_with_fedora/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://fedoramagazine.org/running-rosettahome-on-a-raspberry-pi-with-fedora-iot/", + "subreddit_subscribers": 543995, + "created_utc": 1594210084.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_sx11s", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Getting Things GNOME 0.4 released! First release in almost 7 years (Flatpak available).", + "link_flair_richtext": [{"e": "text", "t": "Software Release"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hn5wh6", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.79, + "author_flair_background_color": "transparent", + "subreddit_type": "public", + "ups": 58, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "2194c338-ce1d-11e8-8ed7-0e20bb1bbc52", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Software Release", + "can_mod_post": False, + "score": 58, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [ + { + "a": ":nix:", + "e": "emoji", + "u": "https://emoji.redditmedia.com/ww1ubcjpqpr11_t5_2qh1a/nix", + } + ], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594193982.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "flathub.org", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://flathub.org/apps/details/org.gnome.GTG", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "904ea3e4-6748-11e7-b925-0ef3dfbb807a", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": ":nix:", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#349e48", + "id": "hn5wh6", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Kanarme", + "discussion_type": None, + "num_comments": 22, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/linux/comments/hn5wh6/getting_things_gnome_04_released_first_release_in/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://flathub.org/apps/details/org.gnome.GTG", + "subreddit_subscribers": 543995, + "created_utc": 1594165182.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_636xx258", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "mpv is not anymore supporting gnome. and the owner reverted the commit again shortly after and then again made a new one, to add the changes", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": True, + "name": "t3_hnnt0v", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 1.0, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 1, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 1, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "gnome", + "selftext": "", + "author_fullname": "t2_33wgs4m3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "mpv is not anymore supporting gnome. and the owner reverted the commit again shortly after and then again made a new one, to add the changes", + "link_flair_richtext": [{"e": "text", "t": "News"}], + "subreddit_name_prefixed": "r/gnome", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hn1s3r", + "quarantine": False, + "link_flair_text_color": "light", + "upvote_ratio": 0.81, + "author_flair_background_color": "transparent", + "subreddit_type": "public", + "ups": 23, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": "1515012e-bed8-11ea-92a7-0eb4e155a177", + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "News", + "can_mod_post": False, + "score": 23, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": "gnome-user", + "author_flair_richtext": [{"e": "text", "t": "GNOMie"}], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594180508.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "richtext", + "domain": "github.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": "confidence", + "banned_at_utc": None, + "url_overridden_by_dest": "https://github.com/mpv-player/mpv/commit/cdaa496314f90412963f2b3211e18df72910066d#commitcomment-40434556", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "7dbe0c80-f9df-11e8-b35e-0e2ae22a2534", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": "GNOMie", + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qjhn", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#692c52", + "id": "hn1s3r", + "is_robot_indexable": True, + "report_reasons": None, + "author": "idiot10000000", + "discussion_type": None, + "num_comments": 53, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/gnome/comments/hn1s3r/mpv_is_not_anymore_supporting_gnome_and_the_owner/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://github.com/mpv-player/mpv/commit/cdaa496314f90412963f2b3211e18df72910066d#commitcomment-40434556", + "subreddit_subscribers": 41350, + "created_utc": 1594151708.0, + "num_crossposts": 1, + "media": None, + "is_video": False, + } + ], + "created": 1594265700.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "github.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://github.com/mpv-player/mpv/commit/cdaa496314f90412963f2b3211e18df72910066d#commitcomment-40434556", + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnnt0v", + "is_robot_indexable": True, + "report_reasons": None, + "author": "RetartedTortoise", + "discussion_type": None, + "num_comments": 0, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hn1s3r", + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnnt0v/mpv_is_not_anymore_supporting_gnome_and_the_owner/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://github.com/mpv-player/mpv/commit/cdaa496314f90412963f2b3211e18df72910066d#commitcomment-40434556", + "subreddit_subscribers": 543995, + "created_utc": 1594236900.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_21omsw7y", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Google and Canonical bring Linux apps support to Flutter - 9to5Google", + "link_flair_richtext": [{"e": "text", "t": "Development"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnj42j", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.59, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 3, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Development", + "can_mod_post": False, + "score": 3, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594251002.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "9to5google.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://9to5google.com/2020/07/08/google-canonical-partnership-linux-flutter-apps/", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "3cb511e2-7914-11ea-bb33-0ee30ee9d22b", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#f0db8a", + "id": "hnj42j", + "is_robot_indexable": True, + "report_reasons": None, + "author": "satvikpendem", + "discussion_type": None, + "num_comments": 1, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnj42j/google_and_canonical_bring_linux_apps_support_to/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://9to5google.com/2020/07/08/google-canonical-partnership-linux-flutter-apps/", + "subreddit_subscribers": 543995, + "created_utc": 1594222202.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": " As far as I understand it, the current options on the Intel Iris/NVIDIA side are:\n\n* Intel or NVIDIA cards only\n\n* Optimus for switching between Intel and Intel+NVIDIA (requires reboot)\n\n* Bumblebee for on-the-fly switching with a performance hit\n\n* nvidia-xrun, which does everything bumblebee can do but requires a second X server\n\n* Prime Rener Offload, a proprietary NVIDIA thing, for switching between Intel and Intel+NVIDIA, which I don't completely understand\n\nDo I have this right? And how do things look on the Amd Vega/Radeon configuration?", + "author_fullname": "t2_tcnt4", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "[Discussion] What's the current status on laptop switchable graphics?", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": True, + "name": "t3_hnmiik", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.67, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 1, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 1, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594261813.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>As far as I understand it, the current options on the Intel Iris/NVIDIA side are:</p>\n\n<ul>\n<li><p>Intel or NVIDIA cards only</p></li>\n<li><p>Optimus for switching between Intel and Intel+NVIDIA (requires reboot)</p></li>\n<li><p>Bumblebee for on-the-fly switching with a performance hit</p></li>\n<li><p>nvidia-xrun, which does everything bumblebee can do but requires a second X server</p></li>\n<li><p>Prime Rener Offload, a proprietary NVIDIA thing, for switching between Intel and Intel+NVIDIA, which I don&#39;t completely understand</p></li>\n</ul>\n\n<p>Do I have this right? And how do things look on the Amd Vega/Radeon configuration?</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnmiik", + "is_robot_indexable": True, + "report_reasons": None, + "author": "KoolDude214", + "discussion_type": None, + "num_comments": 4, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnmiik/discussion_whats_the_current_status_on_laptop/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.reddit.com/r/linux/comments/hnmiik/discussion_whats_the_current_status_on_laptop/", + "subreddit_subscribers": 543995, + "created_utc": 1594233013.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Hello all!\n\nI've created this simple web app as a part of learning web development, to help people select a linux distro for themselves.\n\nIt's a really simple web app, as I've created it as part of learning web development.\n\nIt retrieves data from another API that I've defined and this very API's database is used to store all the releated information that only right now I can store.\n\nAnd this web app is used to get information from that API and display it in an organized way.\n\nHave a look and please let me know about your thoughts and suggestions:\n\nLink: [https://linux-distros-encyclopedia.herokuapp.com/](https://linux-distros-encyclopedia.herokuapp.com/)", + "author_fullname": "t2_4c9tcvx3", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Distributions Encyclopedia Web App", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnlh54", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.5, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 0, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 0, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594258586.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Hello all!</p>\n\n<p>I&#39;ve created this simple web app as a part of learning web development, to help people select a linux distro for themselves.</p>\n\n<p>It&#39;s a really simple web app, as I&#39;ve created it as part of learning web development.</p>\n\n<p>It retrieves data from another API that I&#39;ve defined and this very API&#39;s database is used to store all the releated information that only right now I can store.</p>\n\n<p>And this web app is used to get information from that API and display it in an organized way.</p>\n\n<p>Have a look and please let me know about your thoughts and suggestions:</p>\n\n<p>Link: <a href="https://linux-distros-encyclopedia.herokuapp.com/">https://linux-distros-encyclopedia.herokuapp.com/</a></p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnlh54", + "is_robot_indexable": True, + "report_reasons": None, + "author": "MisterKhJe", + "discussion_type": None, + "num_comments": 2, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnlh54/linux_distributions_encyclopedia_web_app/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.reddit.com/r/linux/comments/hnlh54/linux_distributions_encyclopedia_web_app/", + "subreddit_subscribers": 543995, + "created_utc": 1594229786.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "I would like to turn my old Asus tablet into an ultimate linux-based Ebook reader. It's currently running kali linux due to my netsec background and I can't say that it runs flawlessly. The tablet came with Windows 10 by default. Does anyone have the experience with what distro and pdf reader to use?\n\nIt has to be lightweight due to 1.3Ghz Atom processor and 1Gb of Ram.", + "author_fullname": "t2_y0rlp", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux based Ebook reader tablet", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hnecim", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.56, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 2, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594231304.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>I would like to turn my old Asus tablet into an ultimate linux-based Ebook reader. It&#39;s currently running kali linux due to my netsec background and I can&#39;t say that it runs flawlessly. The tablet came with Windows 10 by default. Does anyone have the experience with what distro and pdf reader to use?</p>\n\n<p>It has to be lightweight due to 1.3Ghz Atom processor and 1Gb of Ram.</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnecim", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Kikur", + "discussion_type": None, + "num_comments": 5, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnecim/linux_based_ebook_reader_tablet/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.reddit.com/r/linux/comments/hnecim/linux_based_ebook_reader_tablet/", + "subreddit_subscribers": 543995, + "created_utc": 1594202504.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_300vb", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Backing up my work-provided Windows laptop with Debian, ZFS and SquashFS", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hn2ro8", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.74, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 23, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 23, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594183686.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "thanassis.space", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://www.thanassis.space/backupCOVID.html", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hn2ro8", + "is_robot_indexable": True, + "report_reasons": None, + "author": "ttsiodras", + "discussion_type": None, + "num_comments": 5, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hn2ro8/backing_up_my_workprovided_windows_laptop_with/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.thanassis.space/backupCOVID.html", + "subreddit_subscribers": 543995, + "created_utc": 1594154886.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "", + "author_fullname": "t2_2ccbdhht", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Debian influences everywhere", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": True, + "name": "t3_hnndj2", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.36, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 0, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 0, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [ + { + "approved_at_utc": None, + "subreddit": "ramen", + "selftext": "", + "author_fullname": "t2_1e5jztuf", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "My 1st Attempt for Tori Paitan", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/ramen", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": True, + "name": "t3_hnn89u", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 1.0, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 2, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": True, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Homemade", + "can_mod_post": False, + "score": 2, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1594263979.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/ai9r2wu5mo951.jpg", + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "28b48e48-ce25-11e8-94f2-0e1ed223bf48", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qykd", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#ffd635", + "id": "hnn89u", + "is_robot_indexable": True, + "report_reasons": None, + "author": "cheesychicken80", + "discussion_type": None, + "num_comments": 1, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/ramen/comments/hnn89u/my_1st_attempt_for_tori_paitan/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/ai9r2wu5mo951.jpg", + "subreddit_subscribers": 257000, + "created_utc": 1594235179.0, + "num_crossposts": 1, + "media": None, + "is_video": False, + } + ], + "created": 1594264403.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.redd.it", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.redd.it/ai9r2wu5mo951.jpg", + "view_count": None, + "archived": False, + "no_follow": True, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hnndj2", + "is_robot_indexable": True, + "report_reasons": None, + "author": "dracardOner", + "discussion_type": None, + "num_comments": 0, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hnn89u", + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hnndj2/debian_influences_everywhere/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.redd.it/ai9r2wu5mo951.jpg", + "subreddit_subscribers": 543995, + "created_utc": 1594235603.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "There is an open issue in Electron-Builder to add option to easily create flatpak repo. This results in many electron apps not officially/easily supporting flatpak, thus solving this would help flatpak adoption and make it easier for users to install their favourite apps. See the issue on github for more info [https://github.com/electron-userland/electron-builder/issues/512](https://github.com/electron-userland/electron-builder/issues/512)\n\nSince there are no technical obstacles that prevent completing this task, I made a small bounty on gitpay [https://gitpay.me/#/task/352](https://gitpay.me/#/task/352) to motivate developers, and if you care about this issue, consider chiming in too, spreading the word or even giving a try at implementing this :)", + "author_fullname": "t2_5hgjidqm", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Crowdsource Flatpak support in Electron-Builder", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hmytic", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.76, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 37, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 37, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594171301.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>There is an open issue in Electron-Builder to add option to easily create flatpak repo. This results in many electron apps not officially/easily supporting flatpak, thus solving this would help flatpak adoption and make it easier for users to install their favourite apps. See the issue on github for more info <a href="https://github.com/electron-userland/electron-builder/issues/512">https://github.com/electron-userland/electron-builder/issues/512</a></p>\n\n<p>Since there are no technical obstacles that prevent completing this task, I made a small bounty on gitpay <a href="https://gitpay.me/#/task/352">https://gitpay.me/#/task/352</a> to motivate developers, and if you care about this issue, consider chiming in too, spreading the word or even giving a try at implementing this :)</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hmytic", + "is_robot_indexable": True, + "report_reasons": None, + "author": "ignapk", + "discussion_type": None, + "num_comments": 23, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hmytic/crowdsource_flatpak_support_in_electronbuilder/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.reddit.com/r/linux/comments/hmytic/crowdsource_flatpak_support_in_electronbuilder/", + "subreddit_subscribers": 543995, + "created_utc": 1594142501.0, + "num_crossposts": 5, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "I was experiencing graphic issues and glitches in some games while using Linux Ubuntu 20.04 LTS with my Ryzen 3 3250u CPU and I wanted to share how I fixed this issue for anyone else with this same problem.\n\nFirst thing you should try is setting 'AMD_DEBUG=nodmacopyimage' as an environmental variable. This only partly fixed the issue for me as most of the in-game textures were still glitchy and messed up. However this method seemed to work for some other people https://gitlab.freedesktop.org/mesa/mesa/-/issues/2814\n\nThe second method I tried was downgrading from Ubuntu 20.04 to Ubuntu 19.10. This fixed my problem instantly and the glitchy in-game textures were no longer an issue.\n\n\nIm still new to Linux and not very tech savvy so I can't provide a detailed explanation of what causes this problem and why these methods seem to fix it however I'm pretty sure its something to do with the AMD graphics drivers. Hopefully this issue will be fixed in the next Ubuntu update\n\nHope this helped ;)", + "author_fullname": "t2_6qntnayu", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Linux Graphical Glitches on Ryzen CPUs", + "link_flair_richtext": [{"e": "text", "t": "Tips and Tricks"}], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hmxiyt", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.79, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 20, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": "Tips and Tricks", + "can_mod_post": False, + "score": 20, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594167246.0, + "link_flair_type": "richtext", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>I was experiencing graphic issues and glitches in some games while using Linux Ubuntu 20.04 LTS with my Ryzen 3 3250u CPU and I wanted to share how I fixed this issue for anyone else with this same problem.</p>\n\n<p>First thing you should try is setting &#39;AMD_DEBUG=nodmacopyimage&#39; as an environmental variable. This only partly fixed the issue for me as most of the in-game textures were still glitchy and messed up. However this method seemed to work for some other people <a href="https://gitlab.freedesktop.org/mesa/mesa/-/issues/2814">https://gitlab.freedesktop.org/mesa/mesa/-/issues/2814</a></p>\n\n<p>The second method I tried was downgrading from Ubuntu 20.04 to Ubuntu 19.10. This fixed my problem instantly and the glitchy in-game textures were no longer an issue.</p>\n\n<p>Im still new to Linux and not very tech savvy so I can&#39;t provide a detailed explanation of what causes this problem and why these methods seem to fix it however I&#39;m pretty sure its something to do with the AMD graphics drivers. Hopefully this issue will be fixed in the next Ubuntu update</p>\n\n<p>Hope this helped ;)</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "link_flair_template_id": "de62f716-76df-11ea-802c-0e7469f68f6b", + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "#00a6a5", + "id": "hmxiyt", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Inolicious_", + "discussion_type": None, + "num_comments": 9, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hmxiyt/linux_graphical_glitches_on_ryzen_cpus/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.reddit.com/r/linux/comments/hmxiyt/linux_graphical_glitches_on_ryzen_cpus/", + "subreddit_subscribers": 543995, + "created_utc": 1594138446.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "linux", + "selftext": "Well, alright, at various points in my life I may have been more pleased, but Windows has been losing my support for years one small nitpick at a time. Just wanted to share the change for whoever cares.\n\n* I liked the look and massive size of the windows less and less \n* As a programmer using bash and zsh on cygwin became more and more annoying\n* Windows keeps randomly changing stuff that I never wanted, like my downloads folder becoming a date-sorted list instead of an actual folder (and switching it back when I changed it!)\n* Adding cortana and the like and making it difficult to disable\n* Windows update.\n* Almost every bit of software I have at this point is also on linux or through a browser!\n\nI switched to Manjaro-Gnome and never looked back.\n\n* It's sleeker/runs faster.\n* Uses less RAM\n* Uses rolling updates\n* I can finally just use a built-in terminal\n* Has an easier to understand file structure, despite its complexity.\n* Is surprisingly easy to use. The only difficult part really was finding the wifi driver, and that was actually because it was mislabeled by the manufacturer.\n* Gnome is definitely nicer to use than Windows 10.\n* Searching for files and programs works well! I really didn't need windows to fail to find a program I had installed and instead offer to search for it online... through Bing on Edge.\n\nI never knew how much bloat Windows had until I switched over. This is so damn nice. I don't know why I didn't consider Linux as a serious alternative until recently. Steam Proton has also come a long, long way, I haven't had issues with a game yet.\n\nAnyways, I just wanted to rant, and I'm probably going to install an Manjaro-xfce on a bunch of old laptops.", + "author_fullname": "t2_8zm4y", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Switched from Windows 10 to Manjaro, never been happier", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/linux", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hmgujt", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.92, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 598, + "total_awards_received": 0, + "media_embed": {}, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 598, + "approved_by": None, + "author_premium": False, + "thumbnail": "", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": None, + "is_self": True, + "mod_note": None, + "created": 1594099445.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "self.linux", + "allow_live_comments": False, + "selftext_html": '<!-- SC_OFF --><div class="md"><p>Well, alright, at various points in my life I may have been more pleased, but Windows has been losing my support for years one small nitpick at a time. Just wanted to share the change for whoever cares.</p>\n\n<ul>\n<li>I liked the look and massive size of the windows less and less </li>\n<li>As a programmer using bash and zsh on cygwin became more and more annoying</li>\n<li>Windows keeps randomly changing stuff that I never wanted, like my downloads folder becoming a date-sorted list instead of an actual folder (and switching it back when I changed it!)</li>\n<li>Adding cortana and the like and making it difficult to disable</li>\n<li>Windows update.</li>\n<li>Almost every bit of software I have at this point is also on linux or through a browser!</li>\n</ul>\n\n<p>I switched to Manjaro-Gnome and never looked back.</p>\n\n<ul>\n<li>It&#39;s sleeker/runs faster.</li>\n<li>Uses less RAM</li>\n<li>Uses rolling updates</li>\n<li>I can finally just use a built-in terminal</li>\n<li>Has an easier to understand file structure, despite its complexity.</li>\n<li>Is surprisingly easy to use. The only difficult part really was finding the wifi driver, and that was actually because it was mislabeled by the manufacturer.</li>\n<li>Gnome is definitely nicer to use than Windows 10.</li>\n<li>Searching for files and programs works well! I really didn&#39;t need windows to fail to find a program I had installed and instead offer to search for it online... through Bing on Edge.</li>\n</ul>\n\n<p>I never knew how much bloat Windows had until I switched over. This is so damn nice. I don&#39;t know why I didn&#39;t consider Linux as a serious alternative until recently. Steam Proton has also come a long, long way, I haven&#39;t had issues with a game yet.</p>\n\n<p>Anyways, I just wanted to rant, and I&#39;m probably going to install an Manjaro-xfce on a bunch of old laptops.</p>\n</div><!-- SC_ON -->', + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1a", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hmgujt", + "is_robot_indexable": True, + "report_reasons": None, + "author": "ForShotgun", + "discussion_type": None, + "num_comments": 213, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/linux/comments/hmgujt/switched_from_windows_10_to_manjaro_never_been/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://www.reddit.com/r/linux/comments/hmgujt/switched_from_windows_10_to_manjaro_never_been/", + "subreddit_subscribers": 543995, + "created_utc": 1594070645.0, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "after": "t3_hmgujt", + "before": None, + }, +} diff --git a/src/newsreader/news/collection/tests/reddit/stream/tests.py b/src/newsreader/news/collection/tests/reddit/stream/tests.py new file mode 100644 index 0000000..19aff61 --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/stream/tests.py @@ -0,0 +1,144 @@ +from json.decoder import JSONDecodeError +from unittest.mock import patch +from uuid import uuid4 + +from django.test import TestCase + +from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.exceptions import ( + StreamDeniedException, + StreamException, + StreamForbiddenException, + StreamNotFoundException, + StreamParseException, + StreamTimeOutException, +) +from newsreader.news.collection.reddit import RedditStream +from newsreader.news.collection.tests.factories import SubredditFactory +from newsreader.news.collection.tests.reddit.stream.mocks import simple_mock + + +class RedditStreamTestCase(TestCase): + def setUp(self): + self.maxDiff = None + + self.patched_fetch = patch("newsreader.news.collection.reddit.fetch") + self.mocked_fetch = self.patched_fetch.start() + + def tearDown(self): + patch.stopall() + + def test_simple_stream(self): + self.mocked_fetch.return_value.json.return_value = simple_mock + + access_token = str(uuid4()) + user = UserFactory(reddit_access_token=access_token) + + subreddit = SubredditFactory(user=user) + stream = RedditStream(subreddit) + + data, stream = stream.read() + + self.assertEquals(data, simple_mock) + self.assertEquals(stream, stream) + self.mocked_fetch.assert_called_once_with( + subreddit.url, headers={"Authorization": f"bearer {access_token}"} + ) + + def test_stream_raises_exception(self): + self.mocked_fetch.side_effect = StreamException + + access_token = str(uuid4()) + user = UserFactory(reddit_access_token=access_token) + + subreddit = SubredditFactory(user=user) + stream = RedditStream(subreddit) + + with self.assertRaises(StreamException): + stream.read() + + self.mocked_fetch.assert_called_once_with( + subreddit.url, headers={"Authorization": f"bearer {access_token}"} + ) + + def test_stream_raises_denied_exception(self): + self.mocked_fetch.side_effect = StreamDeniedException + + access_token = str(uuid4()) + user = UserFactory(reddit_access_token=access_token) + + subreddit = SubredditFactory(user=user) + stream = RedditStream(subreddit) + + with self.assertRaises(StreamDeniedException): + stream.read() + + self.mocked_fetch.assert_called_once_with( + subreddit.url, headers={"Authorization": f"bearer {access_token}"} + ) + + def test_stream_raises_not_found_exception(self): + self.mocked_fetch.side_effect = StreamNotFoundException + + access_token = str(uuid4()) + user = UserFactory(reddit_access_token=access_token) + + subreddit = SubredditFactory(user=user) + stream = RedditStream(subreddit) + + with self.assertRaises(StreamNotFoundException): + stream.read() + + self.mocked_fetch.assert_called_once_with( + subreddit.url, headers={"Authorization": f"bearer {access_token}"} + ) + + def test_stream_raises_time_out_exception(self): + self.mocked_fetch.side_effect = StreamTimeOutException + + access_token = str(uuid4()) + user = UserFactory(reddit_access_token=access_token) + + subreddit = SubredditFactory(user=user) + stream = RedditStream(subreddit) + + with self.assertRaises(StreamTimeOutException): + stream.read() + + self.mocked_fetch.assert_called_once_with( + subreddit.url, headers={"Authorization": f"bearer {access_token}"} + ) + + def test_stream_raises_forbidden_exception(self): + self.mocked_fetch.side_effect = StreamForbiddenException + + access_token = str(uuid4()) + user = UserFactory(reddit_access_token=access_token) + + subreddit = SubredditFactory(user=user) + stream = RedditStream(subreddit) + + with self.assertRaises(StreamForbiddenException): + stream.read() + + self.mocked_fetch.assert_called_once_with( + subreddit.url, headers={"Authorization": f"bearer {access_token}"} + ) + + def test_stream_raises_parse_exception(self): + self.mocked_fetch.return_value.json.side_effect = JSONDecodeError( + "No json found", "{}", 5 + ) + + access_token = str(uuid4()) + user = UserFactory(reddit_access_token=access_token) + + subreddit = SubredditFactory(user=user) + stream = RedditStream(subreddit) + + with self.assertRaises(StreamParseException): + stream.read() + + self.mocked_fetch.assert_called_once_with( + subreddit.url, headers={"Authorization": f"bearer {access_token}"} + ) diff --git a/src/newsreader/news/collection/tests/reddit/test_scheduler.py b/src/newsreader/news/collection/tests/reddit/test_scheduler.py new file mode 100644 index 0000000..0f04d53 --- /dev/null +++ b/src/newsreader/news/collection/tests/reddit/test_scheduler.py @@ -0,0 +1,142 @@ +from datetime import timedelta + +from django.test import TestCase +from django.utils import timezone + +from freezegun import freeze_time + +from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.reddit import RedditScheduler +from newsreader.news.collection.tests.factories import CollectionRuleFactory + + +@freeze_time("2019-10-30 12:30:00") +class RedditSchedulerTestCase(TestCase): + def test_simple(self): + user_1 = UserFactory( + reddit_access_token="1231414", reddit_refresh_token="5235262" + ) + user_2 = UserFactory( + reddit_access_token="3414777", reddit_refresh_token="3423425" + ) + + user_1_rules = [ + CollectionRuleFactory( + user=user_1, + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(days=4), + enabled=True, + ), + CollectionRuleFactory( + user=user_1, + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(days=3), + enabled=True, + ), + CollectionRuleFactory( + user=user_1, + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(days=2), + enabled=True, + ), + ] + + user_2_rules = [ + CollectionRuleFactory( + user=user_2, + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(days=4), + enabled=True, + ), + CollectionRuleFactory( + user=user_2, + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(days=3), + enabled=True, + ), + CollectionRuleFactory( + user=user_2, + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(days=2), + enabled=True, + ), + ] + + scheduler = RedditScheduler() + scheduled_subreddits = scheduler.get_scheduled_rules() + + user_1_batch = [subreddit.pk for subreddit in scheduled_subreddits[0]] + + self.assertIn(user_1_rules[0].pk, user_1_batch) + self.assertIn(user_1_rules[1].pk, user_1_batch) + self.assertIn(user_1_rules[2].pk, user_1_batch) + + user_2_batch = [subreddit.pk for subreddit in scheduled_subreddits[1]] + + self.assertIn(user_2_rules[0].pk, user_2_batch) + self.assertIn(user_2_rules[1].pk, user_2_batch) + self.assertIn(user_2_rules[2].pk, user_2_batch) + + def test_max_amount(self): + users = UserFactory.create_batch( + reddit_access_token="1231414", reddit_refresh_token="5235262", size=5 + ) + + nested_rules = [ + CollectionRuleFactory.create_batch( + name=f"rule-{index}", + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(seconds=index), + enabled=True, + user=user, + size=15, + ) + for index, user in enumerate(users) + ] + + rules = [rule for rule_list in nested_rules for rule in rule_list] + + scheduler = RedditScheduler() + scheduled_subreddits = [ + subreddit.pk + for batch in scheduler.get_scheduled_rules() + for subreddit in batch + ] + + for rule in rules[16:76]: + with self.subTest(rule=rule): + self.assertIn(rule.pk, scheduled_subreddits) + + for rule in rules[0:15]: + with self.subTest(rule=rule): + self.assertNotIn(rule.pk, scheduled_subreddits) + + def test_max_user_amount(self): + user = UserFactory( + reddit_access_token="1231414", reddit_refresh_token="5235262" + ) + + rules = [ + CollectionRuleFactory( + name=f"rule-{index}", + type=RuleTypeChoices.subreddit, + last_run=timezone.now() - timedelta(seconds=index), + enabled=True, + user=user, + ) + for index in range(1, 17) + ] + + scheduler = RedditScheduler() + scheduled_subreddits = [ + subreddit.pk + for batch in scheduler.get_scheduled_rules() + for subreddit in batch + ] + + for rule in rules[1:16]: + with self.subTest(rule=rule): + self.assertIn(rule.pk, scheduled_subreddits) + + self.assertNotIn(rules[0].pk, scheduled_subreddits) diff --git a/src/newsreader/news/collection/tests/twitter/__init__.py b/src/newsreader/news/collection/tests/twitter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/twitter/builder/__init__.py b/src/newsreader/news/collection/tests/twitter/builder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/twitter/builder/mocks.py b/src/newsreader/news/collection/tests/twitter/builder/mocks.py new file mode 100644 index 0000000..2be360d --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/builder/mocks.py @@ -0,0 +1,2386 @@ +# retrieved with: +# curl -X GET -H "Authorization: Bearer " "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=twitterapi&tweet_mode=extended" | python3 -m json.tool --sort-keys +# +# see https://developer.twitter.com/en/docs/twitter-api/v1/data-dictionary/overview/tweet-object +# and https://developer.twitter.com/en/docs/twitter-api/v1/data-dictionary/overview/extended-entities-object +# for more information about tweet objects + +simple_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Aug 07 00:17:05 +0000 2020", + "display_text_range": [11, 59], + "entities": { + "hashtags": [], + "symbols": [], + "urls": [ + { + "display_url": "youtu.be/rDy7tPf6CT8", + "expanded_url": "https://youtu.be/rDy7tPf6CT8", + "indices": [36, 59], + "url": "https://t.co/trAcIxBMlX", + } + ], + "user_mentions": [ + { + "id": 975844884606275587, + "id_str": "975844884606275587", + "indices": [0, 10], + "name": "ArieNeo", + "screen_name": "ArieNeoSC", + } + ], + }, + "favorite_count": 19, + "favorited": False, + "full_text": "@ArieNeoSC Here you go, goodnight!\n\nhttps://t.co/trAcIxBMlX", + "geo": None, + "id": 1291528756373286914, + "id_str": "1291528756373286914", + "in_reply_to_screen_name": "ArieNeoSC", + "in_reply_to_status_id": 1291507356313038850, + "in_reply_to_status_id_str": "1291507356313038850", + "in_reply_to_user_id": 975844884606275587, + "in_reply_to_user_id_str": "975844884606275587", + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 5, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Wed Jul 29 19:01:47 +0000 2020", + "display_text_range": [10, 98], + "entities": { + "hashtags": [], + "symbols": [], + "urls": [], + "user_mentions": [ + { + "id": 435221600, + "id_str": "435221600", + "indices": [0, 9], + "name": "Christopher Blough", + "screen_name": "RelicCcb", + } + ], + }, + "favorite_count": 1, + "favorited": False, + "full_text": "@RelicCcb Hi Christoper, we have checked the status of your investigation and it is still ongoing.", + "geo": None, + "id": 1288550304095416320, + "id_str": "1288550304095416320", + "in_reply_to_screen_name": "RelicCcb", + "in_reply_to_status_id": 1288475147951898625, + "in_reply_to_status_id_str": "1288475147951898625", + "in_reply_to_user_id": 435221600, + "in_reply_to_user_id_str": "435221600", + "is_quote_status": False, + "lang": "en", + "place": None, + "retweet_count": 0, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] + +# contains tweets with "extended_entities" keys which contains native media objects +# which are in this case of the type "photo" +image_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Jun 05 22:51:46 +0000 2020", + "entities": { + "hashtags": [], + "media": [ + { + "display_url": "pic.twitter.com/VjEeDrL1iA", + "expanded_url": "https://twitter.com/knxwledge/status/1269039237166321664/photo/1", + "id": 1269039233072689152, + "id_str": "1269039233072689152", + "indices": [2, 25], + "media_url": "http://pbs.twimg.com/media/EZyIdXVU8AACPCz.jpg", + "media_url_https": "https://pbs.twimg.com/media/EZyIdXVU8AACPCz.jpg", + "sizes": { + "large": {"h": 1073, "resize": "fit", "w": 1125}, + "medium": {"h": 1073, "resize": "fit", "w": 1125}, + "small": {"h": 649, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/VjEeDrL1iA", + } + ], + "symbols": [], + "urls": [], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "display_url": "pic.twitter.com/VjEeDrL1iA", + "expanded_url": "https://twitter.com/knxwledge/status/1269039237166321664/photo/1", + "id": 1269039233072689152, + "id_str": "1269039233072689152", + "indices": [2, 25], + "media_url": "http://pbs.twimg.com/media/EZyIdXVU8AACPCz.jpg", + "media_url_https": "https://pbs.twimg.com/media/EZyIdXVU8AACPCz.jpg", + "sizes": { + "large": {"h": 1073, "resize": "fit", "w": 1125}, + "medium": {"h": 1073, "resize": "fit", "w": 1125}, + "small": {"h": 649, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/VjEeDrL1iA", + }, + { + "display_url": "pic.twitter.com/VjEeDrL1iA", + "expanded_url": "https://twitter.com/knxwledge/status/1269039237166321664/photo/1", + "id": 1269039233068527618, + "id_str": "1269039233068527618", + "indices": [2, 25], + "media_url": "http://pbs.twimg.com/media/EZyIdXUVcAI3Cju.jpg", + "media_url_https": "https://pbs.twimg.com/media/EZyIdXUVcAI3Cju.jpg", + "sizes": { + "large": {"h": 992, "resize": "fit", "w": 1472}, + "medium": {"h": 809, "resize": "fit", "w": 1200}, + "small": {"h": 458, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/VjEeDrL1iA", + }, + ] + }, + "favorite_count": 2139, + "favorited": False, + "geo": None, + "id": 1269039237166321664, + "id_str": "1269039237166321664", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "und", + "place": None, + "possibly_sensitive": False, + "possibly_sensitive_appealable": False, + "retweet_count": 427, + "retweeted": False, + "source": 'Twitter for iPhone', + "full_text": "_ https://t.co/VjEeDrL1iA", + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Tue Nov 14 19:00:00 +0000 2017", + "default_profile": False, + "default_profile_image": False, + "description": "Grammy\u00ae Award Winning Beatmakr. https://t.co/SN23ei3EeC https://t.co/EkGRhZ1Bw9 https://t.co/eEb4NOmJLo", + "entities": { + "description": { + "urls": [ + { + "display_url": "soundcloud.com/knxwledge", + "expanded_url": "http://soundcloud.com/knxwledge", + "indices": [32, 55], + "url": "https://t.co/SN23ei3EeC", + }, + { + "display_url": "knxwledge.bandcamp.com", + "expanded_url": "http://knxwledge.bandcamp.com", + "indices": [56, 79], + "url": "https://t.co/EkGRhZ1Bw9", + }, + { + "display_url": "twitch.tv/knxwledge", + "expanded_url": "http://twitch.tv/knxwledge", + "indices": [80, 103], + "url": "https://t.co/eEb4NOmJLo", + }, + ] + }, + "url": { + "urls": [ + { + "display_url": "instagram.com/knxwledge/?hl=\u2026", + "expanded_url": "https://www.instagram.com/knxwledge/?hl=en", + "indices": [0, 23], + "url": "https://t.co/UcMYfiQXLx", + } + ] + }, + }, + "favourites_count": 363, + "follow_request_sent": None, + "followers_count": 31194, + "following": None, + "friends_count": 15, + "geo_enabled": False, + "has_extended_profile": False, + "id": 930510644763287552, + "id_str": "930510644763287552", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 56, + "location": "", + "name": "knxwledge", + "notifications": None, + "profile_background_color": "000000", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_tile": False, + "profile_image_url": "http://pbs.twimg.com/profile_images/1274913160898592768/jFi4VDtJ_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/1274913160898592768/jFi4VDtJ_normal.jpg", + "profile_link_color": "ABB8C2", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "000000", + "profile_text_color": "000000", + "profile_use_background_image": False, + "protected": False, + "screen_name": "knxwledge", + "statuses_count": 713, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/UcMYfiQXLx", + "utc_offset": None, + "verified": False, + }, + } +] + +# contains tweets with "extended_entities" keys which contains native media objects +# which are in this case of the type "video" +video_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Wed Aug 05 18:36:00 +0000 2020", + "display_text_range": [0, 196], + "entities": { + "hashtags": [], + "media": [ + { + "display_url": "pic.twitter.com/mZ8CAuq3SH", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1291080532361527296/video/1", + "id": 1291074294747770880, + "id_str": "1291074294747770880", + "indices": [197, 220], + "media_url": "http://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "media_url_https": "https://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/mZ8CAuq3SH", + } + ], + "symbols": [], + "urls": [ + { + "display_url": "robertsspaceindustries.com/greycatroc", + "expanded_url": "http://robertsspaceindustries.com/greycatroc", + "indices": [173, 196], + "url": "https://t.co/2aH7qdOfSk", + } + ], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "additional_media_info": { + "description": "", + "embeddable": True, + "monetizable": False, + "title": "", + }, + "display_url": "pic.twitter.com/mZ8CAuq3SH", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1291080532361527296/video/1", + "id": 1291074294747770880, + "id_str": "1291074294747770880", + "indices": [197, 220], + "media_url": "http://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "media_url_https": "https://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "video", + "url": "https://t.co/mZ8CAuq3SH", + "video_info": { + "aspect_ratio": [16, 9], + "duration_millis": 82967, + "variants": [ + { + "content_type": "application/x-mpegURL", + "url": "https://video.twimg.com/amplify_video/1291074294747770880/pl/kMYgFEoRyoW99o-i.m3u8?tag=13", + }, + { + "bitrate": 2176000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/amplify_video/1291074294747770880/vid/1280x720/J05_p6q74ZUN4csg.mp4?tag=13", + }, + { + "bitrate": 832000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/amplify_video/1291074294747770880/vid/640x360/ya3fVKeRdBs3cOoF.mp4?tag=13", + }, + { + "bitrate": 288000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/amplify_video/1291074294747770880/vid/480x270/WQkAozOts-hRoU1I.mp4?tag=13", + }, + ], + }, + } + ] + }, + "favorite_count": 289, + "favorited": False, + "full_text": "Small enough to access hard-to-reach ore deposits, but with enough power to get through the tough jobs, Greycat\u2019s ROC perfectly complements any mining operation. \n\nDetails: https://t.co/2aH7qdOfSk https://t.co/mZ8CAuq3SH", + "geo": None, + "id": 1291080532361527296, + "id_str": "1291080532361527296", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 64, + "retweeted": False, + "source": 'Twitter Media Studio', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Wed Aug 05 18:31:27 +0000 2020", + "display_text_range": [0, 213], + "entities": { + "hashtags": [{"indices": [157, 169], "text": "StarCitizen"}], + "media": [ + { + "display_url": "pic.twitter.com/lri5QijMoA", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1291079386821582849/video/1", + "id": 1291070740347813889, + "id_str": "1291070740347813889", + "indices": [214, 237], + "media_url": "http://pbs.twimg.com/media/EerUMgyUwAAgj9w.jpg", + "media_url_https": "https://pbs.twimg.com/media/EerUMgyUwAAgj9w.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/lri5QijMoA", + } + ], + "symbols": [], + "urls": [ + { + "display_url": "robertsspaceindustries.com/comm-link/tran\u2026", + "expanded_url": "https://robertsspaceindustries.com/comm-link/transmission/17648-Alpha-310-Flight-Fight", + "indices": [190, 213], + "url": "https://t.co/6jT1yuZMiR", + } + ], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "additional_media_info": { + "description": "", + "embeddable": True, + "monetizable": False, + "title": "", + }, + "display_url": "pic.twitter.com/lri5QijMoA", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1291079386821582849/video/1", + "id": 1291070740347813889, + "id_str": "1291070740347813889", + "indices": [214, 237], + "media_url": "http://pbs.twimg.com/media/EerUMgyUwAAgj9w.jpg", + "media_url_https": "https://pbs.twimg.com/media/EerUMgyUwAAgj9w.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "video", + "url": "https://t.co/lri5QijMoA", + "video_info": { + "aspect_ratio": [16, 9], + "duration_millis": 83633, + "variants": [ + { + "bitrate": 288000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/amplify_video/1291070740347813889/vid/480x270/oGdSeLr5QQ-XcTns.mp4?tag=13", + }, + { + "bitrate": 2176000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/amplify_video/1291070740347813889/vid/1280x720/bql0evKsgYZhGPNP.mp4?tag=13", + }, + { + "bitrate": 832000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/amplify_video/1291070740347813889/vid/640x360/lSL6mqB53HnwrUo4.mp4?tag=13", + }, + { + "content_type": "application/x-mpegURL", + "url": "https://video.twimg.com/amplify_video/1291070740347813889/pl/_jJ-AYWSMr8ZS1WP.m3u8?tag=13", + }, + ], + }, + } + ] + }, + "favorite_count": 429, + "favorited": False, + "full_text": "Harness the power of improved high-speed dynamic combat. Feel the thrill of atmospheric flight like never before. Alpha 3.10 will change the way you play. \ud83d\ude80 #StarCitizen\n\nGet in the 'verse: https://t.co/6jT1yuZMiR https://t.co/lri5QijMoA", + "geo": None, + "id": 1291079386821582849, + "id_str": "1291079386821582849", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 117, + "retweeted": False, + "source": 'Twitter Media Studio', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] + +video_without_bitrate_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Wed Aug 05 18:36:00 +0000 2020", + "display_text_range": [0, 196], + "entities": { + "hashtags": [], + "media": [ + { + "display_url": "pic.twitter.com/mZ8CAuq3SH", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1291080532361527296/video/1", + "id": 1291074294747770880, + "id_str": "1291074294747770880", + "indices": [197, 220], + "media_url": "http://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "media_url_https": "https://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/mZ8CAuq3SH", + } + ], + "symbols": [], + "urls": [ + { + "display_url": "robertsspaceindustries.com/greycatroc", + "expanded_url": "http://robertsspaceindustries.com/greycatroc", + "indices": [173, 196], + "url": "https://t.co/2aH7qdOfSk", + } + ], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "additional_media_info": { + "description": "", + "embeddable": True, + "monetizable": False, + "title": "", + }, + "display_url": "pic.twitter.com/mZ8CAuq3SH", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1291080532361527296/video/1", + "id": 1291074294747770880, + "id_str": "1291074294747770880", + "indices": [197, 220], + "media_url": "http://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "media_url_https": "https://pbs.twimg.com/media/EerWyexUEAQhRL1.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "video", + "url": "https://t.co/mZ8CAuq3SH", + "video_info": { + "aspect_ratio": [16, 9], + "duration_millis": 82967, + "variants": [ + { + "content_type": "application/x-mpegURL", + "url": "https://video.twimg.com/amplify_video/1291074294747770880/pl/kMYgFEoRyoW99o-i.m3u8?tag=13", + } + ], + }, + } + ] + }, + "favorite_count": 289, + "favorited": False, + "full_text": "Small enough to access hard-to-reach ore deposits, but with enough power to get through the tough jobs, Greycat\u2019s ROC perfectly complements any mining operation. \n\nDetails: https://t.co/2aH7qdOfSk https://t.co/mZ8CAuq3SH", + "geo": None, + "id": 1291080532361527296, + "id_str": "1291080532361527296", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 64, + "retweeted": False, + "source": 'Twitter Media Studio', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + } +] + +# contains tweets with a "retweeted_status" key containing the retweeted tweet. +# the "retweet" cannot add hashtags, URLs or other details, see https://developer.twitter.com/en/docs/twitter-api/v1/data-dictionary/overview/entities-object#retweets-quote +retweet_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Wed Aug 05 21:01:02 +0000 2020", + "display_text_range": [0, 140], + "entities": { + "hashtags": [{"indices": [27, 39], "text": "StarCitizen"}], + "symbols": [], + "urls": [], + "user_mentions": [ + { + "id": 859293278100914176, + "id_str": "859293278100914176", + "indices": [3, 14], + "name": "Aleksandr Belov", + "screen_name": "Narayan_N7", + } + ], + }, + "favorite_count": 0, + "favorited": False, + "full_text": "RT @Narayan_N7: New video! #StarCitizen 3.9 vs. 3.10 comparison!\nSo, the patch 3.10 came out, which brought us quite a lot of changes!\ud83d\ude42\nPle\u2026", + "geo": None, + "id": 1291117030486106112, + "id_str": "1291117030486106112", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "retweet_count": 26, + "retweeted": False, + "retweeted_status": { + "contributors": None, + "coordinates": None, + "created_at": "Wed Aug 05 18:15:34 +0000 2020", + "display_text_range": [0, 250], + "entities": { + "hashtags": [{"indices": [11, 23], "text": "StarCitizen"}], + "symbols": [], + "urls": [ + { + "display_url": "youtu.be/aXXGnCbEas0", + "expanded_url": "https://youtu.be/aXXGnCbEas0", + "indices": [227, 250], + "url": "https://t.co/j4QahHzbw4", + } + ], + "user_mentions": [ + { + "id": 803542770, + "id_str": "803542770", + "indices": [193, 209], + "name": "Star Citizen", + "screen_name": "RobertsSpaceInd", + }, + { + "id": 803697073, + "id_str": "803697073", + "indices": [211, 225], + "name": "Cloud Imperium Games", + "screen_name": "CloudImperium", + }, + ], + }, + "favorite_count": 97, + "favorited": False, + "full_text": "New video! #StarCitizen 3.9 vs. 3.10 comparison!\nSo, the patch 3.10 came out, which brought us quite a lot of changes!\ud83d\ude42\nPlease, share it with your friends!\ud83d\ude4f\n\nEnjoy watching and stay safe! \u2764\ufe0f\u263a\ufe0f\n@RobertsSpaceInd\n\n@CloudImperium\n\nhttps://t.co/j4QahHzbw4", + "geo": None, + "id": 1291075388798533633, + "id_str": "1291075388798533633", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 26, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Tue May 02 06:27:37 +0000 2017", + "default_profile": True, + "default_profile_image": False, + "description": "Enlist to Star Citizen: https://t.co/JOei50wjGK Content creator. #IWantToWorkAtCIG \n#StarCitizen #video #youtube #flickr #4K #panorama", + "entities": { + "description": { + "urls": [ + { + "display_url": "goo.gl/8CbEZm", + "expanded_url": "http://goo.gl/8CbEZm", + "indices": [24, 47], + "url": "https://t.co/JOei50wjGK", + } + ] + }, + "url": { + "urls": [ + { + "display_url": "youtube.com/user/sashaMOHC\u2026", + "expanded_url": "https://www.youtube.com/user/sashaMOHCTPwhite", + "indices": [0, 23], + "url": "https://t.co/ise14uN9Ja", + } + ] + }, + }, + "favourites_count": 1882, + "follow_request_sent": None, + "followers_count": 489, + "following": None, + "friends_count": 80, + "geo_enabled": True, + "has_extended_profile": True, + "id": 859293278100914176, + "id_str": "859293278100914176", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 16, + "location": "\u0421\u0430\u043d\u043a\u0442-\u041f\u0435\u0442\u0435\u0440\u0431\u0443\u0440\u0433, \u0420\u043e\u0441\u0441\u0438\u044f", + "name": "Aleksandr Belov", + "notifications": None, + "profile_background_color": "F5F8FA", + "profile_background_image_url": None, + "profile_background_image_url_https": None, + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/859293278100914176/1576841460", + "profile_image_url": "http://pbs.twimg.com/profile_images/1203066581573607425/5TEkxVJ3_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/1203066581573607425/5TEkxVJ3_normal.jpg", + "profile_link_color": "1DA1F2", + "profile_sidebar_border_color": "C0DEED", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "Narayan_N7", + "statuses_count": 1283, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/ise14uN9Ja", + "utc_offset": None, + "verified": False, + }, + }, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Thu Jul 30 13:15:25 +0000 2020", + "display_text_range": [0, 140], + "entities": { + "hashtags": [{"indices": [24, 40], "text": "CountdownToMars"}], + "symbols": [], + "urls": [], + "user_mentions": [ + { + "id": 11348282, + "id_str": "11348282", + "indices": [3, 8], + "name": "NASA", + "screen_name": "NASA", + }, + { + "id": 1232783237623119872, + "id_str": "1232783237623119872", + "indices": [123, 137], + "name": "NASA's Perseverance Mars Rover", + "screen_name": "NASAPersevere", + }, + ], + }, + "favorite_count": 0, + "favorited": False, + "full_text": "RT @NASA: LIVE NOW: The #CountdownToMars begins. \n\nWe are launching a historic mission to the Red Planet. Tune in to watch @NASAPersevere l\u2026", + "geo": None, + "id": 1288825524878336000, + "id_str": "1288825524878336000", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "retweet_count": 8867, + "retweeted": False, + "retweeted_status": { + "contributors": None, + "coordinates": None, + "created_at": "Thu Jul 30 11:01:06 +0000 2020", + "display_text_range": [0, 236], + "entities": { + "hashtags": [{"indices": [14, 30], "text": "CountdownToMars"}], + "symbols": [], + "urls": [ + { + "display_url": "twitter.com/i/broadcasts/1\u2026", + "expanded_url": "https://twitter.com/i/broadcasts/1RDGlrkoEzNxL", + "indices": [213, 236], + "url": "https://t.co/JxyRCol01i", + } + ], + "user_mentions": [ + { + "id": 1232783237623119872, + "id_str": "1232783237623119872", + "indices": [113, 127], + "name": "NASA's Perseverance Mars Rover", + "screen_name": "NASAPersevere", + } + ], + }, + "favorite_count": 18327, + "favorited": False, + "full_text": "LIVE NOW: The #CountdownToMars begins. \n\nWe are launching a historic mission to the Red Planet. Tune in to watch @NASAPersevere liftoff and begin her mission to search for signs of ancient life on another world: https://t.co/JxyRCol01i", + "geo": None, + "id": 1288791726165983233, + "id_str": "1288791726165983233", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 8867, + "retweeted": False, + "source": 'Twitter Media Studio', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Dec 19 20:20:32 +0000 2007", + "default_profile": False, + "default_profile_image": False, + "description": "Explore the universe and our home planet with NASA \ud83c\udf0e We usually post in EDT.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "nasa.gov", + "expanded_url": "http://www.nasa.gov/", + "indices": [0, 23], + "url": "https://t.co/HMJJbimQpV", + } + ] + }, + }, + "favourites_count": 11658, + "follow_request_sent": None, + "followers_count": 39440029, + "following": None, + "friends_count": 222, + "geo_enabled": False, + "has_extended_profile": True, + "id": 11348282, + "id_str": "11348282", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 92535, + "location": "", + "name": "NASA", + "notifications": None, + "profile_background_color": "000000", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/11348282/1596217000", + "profile_image_url": "http://pbs.twimg.com/profile_images/1091070803184177153/TI2qItoi_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/1091070803184177153/TI2qItoi_normal.jpg", + "profile_link_color": "205BA7", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "F3F2F2", + "profile_text_color": "000000", + "profile_use_background_image": True, + "protected": False, + "screen_name": "NASA", + "statuses_count": 61920, + "time_zone": None, + "translator_type": "regular", + "url": "https://t.co/HMJJbimQpV", + "utc_offset": None, + "verified": True, + }, + }, + "source": 'Twitter for iPhone', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] + +# contains tweets with a "quoted_status" key containing the quoted tweet. +# quoted tweets can add hashtags, URL's and other details as it adds content "on top" of the quoted tweet see https://developer.twitter.com/en/docs/twitter-api/v1/data-dictionary/overview/entities-object#retweets-quotes +quoted_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Wed Aug 05 00:05:24 +0000 2020", + "display_text_range": [0, 13], + "entities": { + "hashtags": [], + "symbols": [], + "urls": [ + { + "display_url": "twitter.com/hugolisoir/sta\u2026", + "expanded_url": "https://twitter.com/hugolisoir/status/1290778178793897992", + "indices": [14, 37], + "url": "https://t.co/WyznJwCJLp", + } + ], + "user_mentions": [], + }, + "favorite_count": 576, + "favorited": False, + "full_text": "Bonne nuit \ud83c\udf3a\ud83d\udeeb https://t.co/WyznJwCJLp", + "geo": None, + "id": 1290801039075979264, + "id_str": "1290801039075979264", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": True, + "lang": "fr", + "place": None, + "possibly_sensitive": False, + "quoted_status": { + "contributors": None, + "coordinates": None, + "created_at": "Tue Aug 04 22:34:33 +0000 2020", + "display_text_range": [0, 57], + "entities": { + "hashtags": [{"indices": [0, 12], "text": "Starcitizen"}], + "media": [ + { + "display_url": "pic.twitter.com/xCXun68V3r", + "expanded_url": "https://twitter.com/hugolisoir/status/1290778178793897992/video/1", + "id": 1290778053623382017, + "id_str": "1290778053623382017", + "indices": [58, 81], + "media_url": "http://pbs.twimg.com/ext_tw_video_thumb/1290778053623382017/pu/img/FFHKsCa_gYLNrriu.jpg", + "media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/1290778053623382017/pu/img/FFHKsCa_gYLNrriu.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/xCXun68V3r", + } + ], + "symbols": [], + "urls": [], + "user_mentions": [ + { + "id": 803542770, + "id_str": "803542770", + "indices": [41, 57], + "name": "Star Citizen", + "screen_name": "RobertsSpaceInd", + } + ], + }, + "extended_entities": { + "media": [ + { + "additional_media_info": {"monetizable": False}, + "display_url": "pic.twitter.com/xCXun68V3r", + "expanded_url": "https://twitter.com/hugolisoir/status/1290778178793897992/video/1", + "id": 1290778053623382017, + "id_str": "1290778053623382017", + "indices": [58, 81], + "media_url": "http://pbs.twimg.com/ext_tw_video_thumb/1290778053623382017/pu/img/FFHKsCa_gYLNrriu.jpg", + "media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/1290778053623382017/pu/img/FFHKsCa_gYLNrriu.jpg", + "sizes": { + "large": {"h": 720, "resize": "fit", "w": 1280}, + "medium": {"h": 675, "resize": "fit", "w": 1200}, + "small": {"h": 383, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "video", + "url": "https://t.co/xCXun68V3r", + "video_info": { + "aspect_ratio": [16, 9], + "duration_millis": 39901, + "variants": [ + { + "bitrate": 832000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/1290778053623382017/pu/vid/640x360/jYjO0H2SYSycTi-e.mp4?tag=10", + }, + { + "content_type": "application/x-mpegURL", + "url": "https://video.twimg.com/ext_tw_video/1290778053623382017/pu/pl/wFnVMLjVWi7OKy2o.m3u8?tag=10", + }, + { + "bitrate": 2176000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/1290778053623382017/pu/vid/1280x720/H-BXvYdM0AcSKXpk.mp4?tag=10", + }, + { + "bitrate": 256000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/1290778053623382017/pu/vid/480x270/aWhSjP1gK7djKZUK.mp4?tag=10", + }, + ], + }, + } + ] + }, + "favorite_count": 400, + "favorited": False, + "full_text": "#Starcitizen Le jeu est beau. Bonne nuit @RobertsSpaceInd https://t.co/xCXun68V3r", + "geo": None, + "id": 1290778178793897992, + "id_str": "1290778178793897992", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "fr", + "place": None, + "possibly_sensitive": False, + "retweet_count": 76, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Tue Mar 22 12:00:36 +0000 2011", + "default_profile": False, + "default_profile_image": False, + "description": "Youtuber Partner / Twitch Partner / Membre du @CurryClub_CC\nInsta - hugolisoir\nParrain de @AbyssalProject", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "youtube.com/channel/UCDC6D\u2026", + "expanded_url": "https://www.youtube.com/channel/UCDC6DBi0kRp6Jk21xqfvFLA", + "indices": [0, 23], + "url": "https://t.co/p3CVR2I068", + } + ] + }, + }, + "favourites_count": 20935, + "follow_request_sent": None, + "followers_count": 23269, + "following": None, + "friends_count": 703, + "geo_enabled": True, + "has_extended_profile": False, + "id": 270320632, + "id_str": "270320632", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 116, + "location": "Nantes, France", + "name": "Hugo Lisoir #ZLAN2020", + "notifications": None, + "profile_background_color": "000000", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme15/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme15/bg.png", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/270320632/1499086260", + "profile_image_url": "http://pbs.twimg.com/profile_images/1264841251305730048/vyUJVCvW_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/1264841251305730048/vyUJVCvW_normal.jpg", + "profile_link_color": "ABB8C2", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "000000", + "profile_text_color": "000000", + "profile_use_background_image": False, + "protected": False, + "screen_name": "hugolisoir", + "statuses_count": 7507, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/p3CVR2I068", + "utc_offset": None, + "verified": False, + }, + }, + "quoted_status_id": 1290778178793897992, + "quoted_status_id_str": "1290778178793897992", + "quoted_status_permalink": { + "display": "twitter.com/hugolisoir/sta\u2026", + "expanded": "https://twitter.com/hugolisoir/status/1290778178793897992", + "url": "https://t.co/WyznJwCJLp", + }, + "retweet_count": 60, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Jul 31 22:00:55 +0000 2020", + "display_text_range": [0, 32], + "entities": { + "hashtags": [], + "symbols": [], + "urls": [ + { + "display_url": "twitter.com/UberFacts/stat\u2026", + "expanded_url": "https://twitter.com/UberFacts/status/1289273883493675009", + "indices": [33, 56], + "url": "https://t.co/LLPVr8oU7F", + } + ], + "user_mentions": [], + }, + "favorite_count": 263, + "favorited": False, + "full_text": "Here's to our lovely Avocados! \ud83d\udd79 https://t.co/LLPVr8oU7F", + "geo": None, + "id": 1289320160021495809, + "id_str": "1289320160021495809", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": True, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "quoted_status": { + "contributors": None, + "coordinates": None, + "created_at": "Fri Jul 31 18:57:02 +0000 2020", + "display_text_range": [0, 34], + "entities": { + "hashtags": [], + "media": [ + { + "display_url": "pic.twitter.com/8QRycx9QB2", + "expanded_url": "https://twitter.com/UberFacts/status/1289273883493675009/photo/1", + "id": 1289273880570363907, + "id_str": "1289273880570363907", + "indices": [35, 58], + "media_url": "http://pbs.twimg.com/tweet_video_thumb/EeRrw3WWAAMKVF0.jpg", + "media_url_https": "https://pbs.twimg.com/tweet_video_thumb/EeRrw3WWAAMKVF0.jpg", + "sizes": { + "large": {"h": 500, "resize": "fit", "w": 500}, + "medium": {"h": 500, "resize": "fit", "w": 500}, + "small": {"h": 500, "resize": "fit", "w": 500}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/8QRycx9QB2", + } + ], + "symbols": [], + "urls": [], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "display_url": "pic.twitter.com/8QRycx9QB2", + "expanded_url": "https://twitter.com/UberFacts/status/1289273883493675009/photo/1", + "id": 1289273880570363907, + "id_str": "1289273880570363907", + "indices": [35, 58], + "media_url": "http://pbs.twimg.com/tweet_video_thumb/EeRrw3WWAAMKVF0.jpg", + "media_url_https": "https://pbs.twimg.com/tweet_video_thumb/EeRrw3WWAAMKVF0.jpg", + "sizes": { + "large": {"h": 500, "resize": "fit", "w": 500}, + "medium": {"h": 500, "resize": "fit", "w": 500}, + "small": {"h": 500, "resize": "fit", "w": 500}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "animated_gif", + "url": "https://t.co/8QRycx9QB2", + "video_info": { + "aspect_ratio": [1, 1], + "variants": [ + { + "bitrate": 0, + "content_type": "video/mp4", + "url": "https://video.twimg.com/tweet_video/EeRrw3WWAAMKVF0.mp4", + } + ], + }, + } + ] + }, + "favorite_count": 1550, + "favorited": False, + "full_text": "July 31st is National Avocado Day! https://t.co/8QRycx9QB2", + "geo": None, + "id": 1289273883493675009, + "id_str": "1289273883493675009", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 380, + "retweeted": False, + "source": 'Buffer', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Sun Dec 06 16:07:01 +0000 2009", + "default_profile": False, + "default_profile_image": False, + "description": "The most unimportant things you'll never need to know.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "uber-facts.com", + "expanded_url": "http://uber-facts.com/", + "indices": [0, 23], + "url": "https://t.co/3ycpGqEL9n", + } + ] + }, + }, + "favourites_count": 1297, + "follow_request_sent": None, + "followers_count": 13810392, + "following": None, + "friends_count": 1, + "geo_enabled": True, + "has_extended_profile": False, + "id": 95023423, + "id_str": "95023423", + "is_translation_enabled": True, + "is_translator": False, + "lang": None, + "listed_count": 15141, + "location": "Worldwide!", + "name": "UberFacts", + "notifications": None, + "profile_background_color": "C0DEED", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/95023423/1587338728", + "profile_image_url": "http://pbs.twimg.com/profile_images/615696617165885440/JDbUuo9H_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/615696617165885440/JDbUuo9H_normal.jpg", + "profile_link_color": "0D9BA8", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "FFFFFF", + "profile_text_color": "000000", + "profile_use_background_image": True, + "protected": False, + "screen_name": "UberFacts", + "statuses_count": 202253, + "time_zone": None, + "translator_type": "regular", + "url": "https://t.co/3ycpGqEL9n", + "utc_offset": None, + "verified": True, + }, + }, + "quoted_status_id": 1289273883493675009, + "quoted_status_id_str": "1289273883493675009", + "quoted_status_permalink": { + "display": "twitter.com/UberFacts/stat\u2026", + "expanded": "https://twitter.com/UberFacts/status/1289273883493675009", + "url": "https://t.co/LLPVr8oU7F", + }, + "retweet_count": 24, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] + +# contains tweets with "extended_entities" keys which contains native media objects +# which are in this case of the type "animated_gif" +gif_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Jul 31 23:10:55 +0000 2020", + "display_text_range": [12, 12], + "entities": { + "hashtags": [], + "media": [ + { + "display_url": "pic.twitter.com/wxvioLCJ6h", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1289337776140296193/photo/1", + "id": 1289337769521606656, + "id_str": "1289337769521606656", + "indices": [13, 36], + "media_url": "http://pbs.twimg.com/tweet_video_thumb/EeSl3sPUcAAyE4J.jpg", + "media_url_https": "https://pbs.twimg.com/tweet_video_thumb/EeSl3sPUcAAyE4J.jpg", + "sizes": { + "large": {"h": 210, "resize": "fit", "w": 250}, + "medium": {"h": 210, "resize": "fit", "w": 250}, + "small": {"h": 210, "resize": "fit", "w": 250}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/wxvioLCJ6h", + } + ], + "symbols": [], + "urls": [], + "user_mentions": [ + { + "id": 994361231057346561, + "id_str": "994361231057346561", + "indices": [0, 12], + "name": "Xenosystems", + "screen_name": "Xenosystems", + } + ], + }, + "extended_entities": { + "media": [ + { + "display_url": "pic.twitter.com/wxvioLCJ6h", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1289337776140296193/photo/1", + "id": 1289337769521606656, + "id_str": "1289337769521606656", + "indices": [13, 36], + "media_url": "http://pbs.twimg.com/tweet_video_thumb/EeSl3sPUcAAyE4J.jpg", + "media_url_https": "https://pbs.twimg.com/tweet_video_thumb/EeSl3sPUcAAyE4J.jpg", + "sizes": { + "large": {"h": 210, "resize": "fit", "w": 250}, + "medium": {"h": 210, "resize": "fit", "w": 250}, + "small": {"h": 210, "resize": "fit", "w": 250}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "animated_gif", + "url": "https://t.co/wxvioLCJ6h", + "video_info": { + "aspect_ratio": [25, 21], + "variants": [ + { + "bitrate": 0, + "content_type": "video/mp4", + "url": "https://video.twimg.com/tweet_video/EeSl3sPUcAAyE4J.mp4", + } + ], + }, + } + ] + }, + "favorite_count": 13, + "favorited": False, + "full_text": "@Xenosystems https://t.co/wxvioLCJ6h", + "geo": None, + "id": 1289337776140296193, + "id_str": "1289337776140296193", + "in_reply_to_screen_name": "Xenosystems", + "in_reply_to_status_id": 1289324787815178242, + "in_reply_to_status_id_str": "1289324787815178242", + "in_reply_to_user_id": 994361231057346561, + "in_reply_to_user_id_str": "994361231057346561", + "is_quote_status": False, + "lang": "und", + "place": None, + "possibly_sensitive": False, + "retweet_count": 1, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Thu Jul 30 22:30:29 +0000 2020", + "display_text_range": [12, 12], + "entities": { + "hashtags": [], + "media": [ + { + "display_url": "pic.twitter.com/DTbhK1pTc4", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1288965215648849920/photo/1", + "id": 1288965209596420097, + "id_str": "1288965209596420097", + "indices": [13, 36], + "media_url": "http://pbs.twimg.com/tweet_video_thumb/EeNTB2XU4AE-z5Y.jpg", + "media_url_https": "https://pbs.twimg.com/tweet_video_thumb/EeNTB2XU4AE-z5Y.jpg", + "sizes": { + "large": {"h": 278, "resize": "fit", "w": 498}, + "medium": {"h": 278, "resize": "fit", "w": 498}, + "small": {"h": 278, "resize": "fit", "w": 498}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/DTbhK1pTc4", + } + ], + "symbols": [], + "urls": [], + "user_mentions": [ + { + "id": 994361231057346561, + "id_str": "994361231057346561", + "indices": [0, 12], + "name": "Xenosystems", + "screen_name": "Xenosystems", + } + ], + }, + "extended_entities": { + "media": [ + { + "display_url": "pic.twitter.com/DTbhK1pTc4", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1288965215648849920/photo/1", + "id": 1288965209596420097, + "id_str": "1288965209596420097", + "indices": [13, 36], + "media_url": "http://pbs.twimg.com/tweet_video_thumb/EeNTB2XU4AE-z5Y.jpg", + "media_url_https": "https://pbs.twimg.com/tweet_video_thumb/EeNTB2XU4AE-z5Y.jpg", + "sizes": { + "large": {"h": 278, "resize": "fit", "w": 498}, + "medium": {"h": 278, "resize": "fit", "w": 498}, + "small": {"h": 278, "resize": "fit", "w": 498}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "animated_gif", + "url": "https://t.co/DTbhK1pTc4", + "video_info": { + "aspect_ratio": [249, 139], + "variants": [ + { + "bitrate": 0, + "content_type": "video/mp4", + "url": "https://video.twimg.com/tweet_video/EeNTB2XU4AE-z5Y.mp4", + } + ], + }, + } + ] + }, + "favorite_count": 20, + "favorited": False, + "full_text": "@Xenosystems https://t.co/DTbhK1pTc4", + "geo": None, + "id": 1288965215648849920, + "id_str": "1288965215648849920", + "in_reply_to_screen_name": "Xenosystems", + "in_reply_to_status_id": 1288960722349719554, + "in_reply_to_status_id_str": "1288960722349719554", + "in_reply_to_user_id": 994361231057346561, + "in_reply_to_user_id_str": "994361231057346561", + "is_quote_status": False, + "lang": "und", + "place": None, + "possibly_sensitive": False, + "retweet_count": 0, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] + +unsanitized_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Aug 07 00:17:05 +0000 2020", + "display_text_range": [11, 59], + "entities": { + "hashtags": [], + "symbols": [], + "urls": [ + { + "display_url": "youtu.be/rDy7tPf6CT8", + "expanded_url": "https://youtu.be/rDy7tPf6CT8", + "indices": [36, 59], + "url": "https://t.co/trAcIxBMlX", + } + ], + "user_mentions": [ + { + "id": 975844884606275587, + "id_str": "975844884606275587", + "indices": [0, 10], + "name": "ArieNeo", + "screen_name": "ArieNeoSC", + } + ], + }, + "favorite_count": 19, + "favorited": False, + "full_text": "@ArieNeoSC Here you go, goodnight!\n\nhttps://t.co/trAcIxBMlX
    ", + "geo": None, + "id": 1291528756373286914, + "id_str": "1291528756373286914", + "in_reply_to_screen_name": "ArieNeoSC", + "in_reply_to_status_id": 1291507356313038850, + "in_reply_to_status_id_str": "1291507356313038850", + "in_reply_to_user_id": 975844884606275587, + "in_reply_to_user_id_str": "975844884606275587", + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 5, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + } +] + +broken_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Aug 07 00:17:05 +0000 2020", + "display_text_range": [11, 59], + "entities": { + "hashtags": [], + "symbols": [], + "urls": [ + { + "display_url": "youtu.be/rDy7tPf6CT8", + "expanded_url": "https://youtu.be/rDy7tPf6CT8", + "indices": [36, 59], + "url": "https://t.co/trAcIxBMlX", + } + ], + "user_mentions": [ + { + "id": 975844884606275587, + "id_str": "975844884606275587", + "indices": [0, 10], + "name": "ArieNeo", + "screen_name": "ArieNeoSC", + } + ], + }, + "favorite_count": 19, + "favorited": False, + # Note the missing full_text key here + "geo": None, + "id": 1291528756373286914, + "id_str": "1291528756373286914", + "in_reply_to_screen_name": "ArieNeoSC", + "in_reply_to_status_id": 1291507356313038850, + "in_reply_to_status_id_str": "1291507356313038850", + "in_reply_to_user_id": 975844884606275587, + "in_reply_to_user_id_str": "975844884606275587", + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 5, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Wed Jul 29 19:01:47 +0000 2020", + "display_text_range": [10, 98], + "entities": { + "hashtags": [], + "symbols": [], + "urls": [], + "user_mentions": [ + { + "id": 435221600, + "id_str": "435221600", + "indices": [0, 9], + "name": "Christopher Blough", + "screen_name": "RelicCcb", + } + ], + }, + "favorite_count": 1, + "favorited": False, + "full_text": "@RelicCcb Hi Christoper, we have checked the status of your investigation and it is still ongoing.", + "geo": None, + "id": 1288550304095416320, + "id_str": "1288550304095416320", + "in_reply_to_screen_name": "RelicCcb", + "in_reply_to_status_id": 1288475147951898625, + "in_reply_to_status_id_str": "1288475147951898625", + "in_reply_to_user_id": 435221600, + "in_reply_to_user_id_str": "435221600", + "is_quote_status": False, + "lang": "en", + "place": None, + "retweet_count": 0, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4588, + "follow_request_sent": None, + "followers_count": 106169, + "following": None, + "friends_count": 201, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 890, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6210, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] diff --git a/src/newsreader/news/collection/tests/twitter/builder/tests.py b/src/newsreader/news/collection/tests/twitter/builder/tests.py new file mode 100644 index 0000000..2e9ecc0 --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/builder/tests.py @@ -0,0 +1,431 @@ +from datetime import datetime +from unittest.mock import Mock + +from django.test import TestCase +from django.utils.safestring import mark_safe + +import pytz + +from ftfy import fix_text + +from newsreader.news.collection.tests.factories import TwitterTimelineFactory +from newsreader.news.collection.tests.twitter.builder.mocks import ( + broken_mock, + gif_mock, + image_mock, + quoted_mock, + retweet_mock, + simple_mock, + unsanitized_mock, + video_mock, + video_without_bitrate_mock, +) +from newsreader.news.collection.twitter import TWITTER_URL, TwitterBuilder +from newsreader.news.collection.utils import truncate_text +from newsreader.news.core.models import Post +from newsreader.news.core.tests.factories import PostFactory + + +class TwitterBuilderTestCase(TestCase): + def setUp(self): + self.maxDiff = None + + def test_simple_post(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(simple_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual( + ("1291528756373286914", "1288550304095416320"), posts.keys() + ) + + post = posts["1291528756373286914"] + + full_text = ( + "@ArieNeoSC Here you go, goodnight!\n\n" + """https://t.co/trAcIxBMlX""" + ) + + self.assertEquals(post.rule, profile) + self.assertEquals( + post.title, + truncate_text( + Post, + "title", + "@ArieNeoSC Here you go, goodnight!\n\nhttps://t.co/trAcIxBMlX", + ), + ) + self.assertEquals(post.body, mark_safe(full_text)) + + self.assertEquals(post.author, "RobertsSpaceInd") + self.assertEquals( + post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1291528756373286914" + ) + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 8, 7, 0, 17, 5)) + ) + + post = posts["1288550304095416320"] + + full_text = "@RelicCcb Hi Christoper, we have checked the status of your investigation and it is still ongoing." + + self.assertEquals(post.rule, profile) + self.assertEquals(post.title, truncate_text(Post, "title", full_text)) + self.assertEquals(post.body, mark_safe(full_text)) + + self.assertEquals(post.author, "RobertsSpaceInd") + self.assertEquals( + post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1288550304095416320" + ) + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 7, 29, 19, 1, 47)) + ) + + # note that only one media type can be uploaded to an Tweet + # see https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/extended-entities-object + def test_images_in_post(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(image_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("1269039237166321664",), posts.keys()) + + post = posts["1269039237166321664"] + + self.assertEquals(post.rule, profile) + self.assertEquals(post.title, "_ https://t.co/VjEeDrL1iA") + + self.assertEquals(post.author, "RobertsSpaceInd") + self.assertEquals( + post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1269039237166321664" + ) + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 6, 5, 22, 51, 46)) + ) + + self.assertInHTML( + """https://t.co/VjEeDrL1iA""", + post.body, + count=1, + ) + self.assertInHTML( + """
    1269039233072689152
    """, + post.body, + count=1, + ) + self.assertInHTML( + """
    1269039233068527618
    """, + post.body, + count=1, + ) + + def test_videos_in_post(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(video_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual( + ("1291080532361527296", "1291079386821582849"), posts.keys() + ) + + post = posts["1291080532361527296"] + + full_text = fix_text( + "Small enough to access hard-to-reach ore deposits, but with enough" + " power to get through the tough jobs, Greycat\u2019s ROC perfectly" + " complements any mining operation. \n\nDetails:" + """ https://t.co/2aH7qdOfSk""" + """ https://t.co/mZ8CAuq3SH""" + ) + + self.assertEquals(post.rule, profile) + self.assertEquals( + post.title, + truncate_text( + Post, + "title", + fix_text( + "Small enough to access hard-to-reach ore deposits, but with enough" + " power to get through the tough jobs, Greycat\u2019s ROC perfectly" + " complements any mining operation. \n\nDetails:" + " https://t.co/2aH7qdOfSk https://t.co/mZ8CAuq3SH" + ), + ), + ) + + self.assertEquals(post.author, "RobertsSpaceInd") + self.assertEquals( + post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1291080532361527296" + ) + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 8, 5, 18, 36, 0)) + ) + + self.assertIn(full_text, post.body) + self.assertInHTML( + """
    """, + post.body, + count=1, + ) + + def test_video_without_bitrate(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(video_without_bitrate_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("1291080532361527296",), posts.keys()) + + post = posts["1291080532361527296"] + + self.assertInHTML( + """
    """, + post.body, + count=1, + ) + + def test_GIFs_in_post(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(gif_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual( + ("1289337776140296193", "1288965215648849920"), posts.keys() + ) + + post = posts["1289337776140296193"] + + self.assertInHTML( + """
    """, + post.body, + count=1, + ) + + self.assertIn( + """@Xenosystems https://t.co/wxvioLCJ6h""", + post.body, + ) + + def test_retweet_post(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(retweet_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual( + ("1291117030486106112", "1288825524878336000"), posts.keys() + ) + + post = posts["1291117030486106112"] + + self.assertIn( + fix_text( + "RT @Narayan_N7: New video! #StarCitizen 3.9 vs. 3.10 comparison!\nSo," + " the patch 3.10 came out, which brought us quite a lot of changes!\ud83d\ude42\nPle\u2026" + ), + post.body, + ) + + self.assertIn( + fix_text( + "Original tweet: New video! #StarCitizen 3.9 vs. 3.10 comparison!\nSo, the patch" + " 3.10 came out, which brought us quite a lot of changes!\ud83d\ude42\nPlease," + " share it with your friends!\ud83d\ude4f\n\nEnjoy watching and stay safe!" + " \u2764\ufe0f\u263a\ufe0f\n@RobertsSpaceInd\n\n@CloudImperium\n\n" + """https://t.co/j4QahHzbw4""" + ), + post.body, + ) + + def test_quoted_post(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(quoted_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual( + ("1290801039075979264", "1289320160021495809"), posts.keys() + ) + + post = posts["1290801039075979264"] + + self.assertIn( + fix_text( + "Bonne nuit \ud83c\udf3a\ud83d\udeeb" + """ https://t.co/WyznJwCJLp""" + ), + post.body, + ) + + self.assertIn( + fix_text( + "Quoted tweet: #Starcitizen Le jeu est beau. Bonne nuit" + """ @RobertsSpaceInd https://t.co/xCXun68V3r""" + ), + post.body, + ) + + def test_empty_data(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder([], mock_stream) as builder: + builder.build() + builder.save() + + self.assertEquals(Post.objects.count(), 0) + + def test_html_sanitizing(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(unsanitized_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("1291528756373286914",), posts.keys()) + + post = posts["1291528756373286914"] + + full_text = ( + "@ArieNeoSC Here you go, goodnight!\n\n" + """https://t.co/trAcIxBMlX""" + "
    " + ) + + self.assertEquals(post.rule, profile) + self.assertEquals( + post.title, + truncate_text( + Post, + "title", + "@ArieNeoSC Here you go, goodnight!\n\nhttps://t.co/trAcIxBMlX" + "
    ", + ), + ) + self.assertEquals(post.body, mark_safe(full_text)) + + self.assertInHTML("", post.body, count=0) + self.assertInHTML("
    ", post.body, count=1) + + self.assertInHTML("", post.title, count=0) + self.assertInHTML("
    ", post.title, count=1) + + def test_urlize_on_urls(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(simple_mock, mock_stream) as builder: + builder.build() + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual( + ("1291528756373286914", "1288550304095416320"), posts.keys() + ) + + post = posts["1291528756373286914"] + + full_text = ( + "@ArieNeoSC Here you go, goodnight!\n\n" + """https://t.co/trAcIxBMlX""" + ) + + self.assertEquals(post.rule, profile) + self.assertEquals( + post.title, + truncate_text( + Post, + "title", + "@ArieNeoSC Here you go, goodnight!\n\nhttps://t.co/trAcIxBMlX", + ), + ) + self.assertEquals(post.body, mark_safe(full_text)) + + def test_existing_posts(self): + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + PostFactory(rule=profile, remote_identifier="1291528756373286914") + PostFactory(rule=profile, remote_identifier="1288550304095416320") + + with builder(simple_mock, mock_stream) as builder: + builder.build() + builder.save() + + self.assertEquals(Post.objects.count(), 2) + + def test_bad_post(self): + """ + Tests that the builder will ignore posts which miss data + """ + builder = TwitterBuilder + + profile = TwitterTimelineFactory(screen_name="RobertsSpaceInd") + mock_stream = Mock(rule=profile) + + with builder(broken_mock, mock_stream) as builder: + builder.build() + builder.save() + + self.assertCountEqual( + Post.objects.values_list("remote_identifier", flat=True), + ["1288550304095416320"], + ) diff --git a/src/newsreader/news/collection/tests/twitter/client/__init__.py b/src/newsreader/news/collection/tests/twitter/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/twitter/client/mocks.py b/src/newsreader/news/collection/tests/twitter/client/mocks.py new file mode 100644 index 0000000..1b7c6a2 --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/client/mocks.py @@ -0,0 +1,225 @@ +# retrieved with: +# curl -X GET -H "Authorization: Bearer " "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=RobertsSpaceInd&tweet_mode=extended" | python3 -m json.tool --sort-keys + +simple_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Sep 18 20:32:22 +0000 2020", + "display_text_range": [0, 111], + "entities": { + "hashtags": [{"indices": [26, 41], "text": "SCShipShowdown"}], + "symbols": [], + "urls": [], + "user_mentions": [], + }, + "favorite_count": 54, + "favorited": False, + "full_text": "It's a close match-up for #SCShipShowdown today! Which Aegis ship do you think will make it to the Semi-Finals?", + "geo": None, + "id": 1307054882210435074, + "id_str": "1307054882210435074", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "retweet_count": 9, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4831, + "follow_request_sent": None, + "followers_count": 106971, + "following": None, + "friends_count": 204, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 893, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6368, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Sep 18 18:50:11 +0000 2020", + "display_text_range": [0, 271], + "entities": { + "hashtags": [{"indices": [211, 218], "text": "Twitch"}], + "media": [ + { + "display_url": "pic.twitter.com/Cey5JpR1i9", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1307029168941461504/photo/1", + "id": 1307028141697765376, + "id_str": "1307028141697765376", + "indices": [272, 295], + "media_url": "http://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "media_url_https": "https://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "sizes": { + "large": {"h": 1090, "resize": "fit", "w": 1920}, + "medium": {"h": 681, "resize": "fit", "w": 1200}, + "small": {"h": 386, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/Cey5JpR1i9", + } + ], + "symbols": [], + "urls": [ + { + "display_url": "twitch.tv/starcitizen", + "expanded_url": "http://twitch.tv/starcitizen", + "indices": [248, 271], + "url": "https://t.co/2AdNovhpFW", + } + ], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "display_url": "pic.twitter.com/Cey5JpR1i9", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1307029168941461504/photo/1", + "id": 1307028141697765376, + "id_str": "1307028141697765376", + "indices": [272, 295], + "media_url": "http://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "media_url_https": "https://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "sizes": { + "large": {"h": 1090, "resize": "fit", "w": 1920}, + "medium": {"h": 681, "resize": "fit", "w": 1200}, + "small": {"h": 386, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/Cey5JpR1i9", + } + ] + }, + "favorite_count": 90, + "favorited": False, + "full_text": "We\u2019re welcoming members of our Builds, Publishes and Platform teams on Star Citizen Live to talk about the process involved in bringing everyone\u2019s work together and getting it out into your hands. Going live on #Twitch in 10 minutes. \ud83c\udfa5\ud83d\udd34 \n\nTune in: https://t.co/2AdNovhpFW https://t.co/Cey5JpR1i9", + "geo": None, + "id": 1307029168941461504, + "id_str": "1307029168941461504", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 13, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4831, + "follow_request_sent": None, + "followers_count": 106971, + "following": None, + "friends_count": 204, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 893, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6368, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] diff --git a/src/newsreader/news/collection/tests/twitter/client/tests.py b/src/newsreader/news/collection/tests/twitter/client/tests.py new file mode 100644 index 0000000..387ffef --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/client/tests.py @@ -0,0 +1,162 @@ +from unittest.mock import Mock, patch +from uuid import uuid4 + +from django.test import TestCase +from django.utils.lorem_ipsum import words + +from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.exceptions import ( + StreamDeniedException, + StreamException, + StreamNotFoundException, + StreamParseException, + StreamTimeOutException, + StreamTooManyException, +) +from newsreader.news.collection.tests.factories import TwitterTimelineFactory +from newsreader.news.collection.twitter import TwitterClient + +from .mocks import simple_mock + + +class TwitterClientTestCase(TestCase): + def setUp(self): + patched_read = patch("newsreader.news.collection.twitter.TwitterStream.read") + self.mocked_read = patched_read.start() + + def tearDown(self): + patch.stopall() + + def test_simple(self): + timeline = TwitterTimelineFactory() + mock_stream = Mock(rule=timeline) + + self.mocked_read.return_value = (simple_mock, mock_stream) + + with TwitterClient([timeline]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertEquals(data, simple_mock) + self.assertEquals(stream, mock_stream) + + self.mocked_read.assert_called() + + def test_client_catches_stream_exception(self): + timeline = TwitterTimelineFactory() + + self.mocked_read.side_effect = StreamException(message="Stream exception") + + with TwitterClient([timeline]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertIsNone(data) + self.assertIsNone(stream) + self.assertEquals(stream.rule.error, "Stream exception") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called() + + def test_client_catches_stream_not_found_exception(self): + timeline = TwitterTimelineFactory.create() + + self.mocked_read.side_effect = StreamNotFoundException( + message="Stream not found" + ) + + with TwitterClient([timeline]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertIsNone(data) + self.assertIsNone(stream) + self.assertEquals(stream.rule.error, "Stream not found") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called() + + def test_client_catches_stream_denied_exception(self): + user = UserFactory( + twitter_oauth_token=str(uuid4()), twitter_oauth_token_secret=str(uuid4()) + ) + timeline = TwitterTimelineFactory(user=user) + + self.mocked_read.side_effect = StreamDeniedException(message="Token expired") + + with TwitterClient([timeline]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertIsNone(data) + self.assertIsNone(stream) + self.assertEquals(stream.rule.error, "Token expired") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called() + + user.refresh_from_db() + timeline.refresh_from_db() + + self.assertIsNone(user.twitter_oauth_token) + self.assertIsNone(user.twitter_oauth_token_secret) + + def test_client_catches_stream_timed_out_exception(self): + timeline = TwitterTimelineFactory() + + self.mocked_read.side_effect = StreamTimeOutException( + message="Stream timed out" + ) + + with TwitterClient([timeline]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertIsNone(data) + self.assertIsNone(stream) + self.assertEquals(stream.rule.error, "Stream timed out") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called() + + def test_client_catches_stream_too_many_exception(self): + timeline = TwitterTimelineFactory() + + self.mocked_read.side_effect = StreamTooManyException + + with TwitterClient([timeline]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertIsNone(data) + self.assertIsNone(stream) + self.assertEquals(stream.rule.error, "Too many requests") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called() + + def test_client_catches_stream_parse_exception(self): + timeline = TwitterTimelineFactory() + + self.mocked_read.side_effect = StreamParseException( + message="Stream could not be parsed" + ) + + with TwitterClient([timeline]) as client: + for data, stream in client: + with self.subTest(data=data, stream=stream): + self.assertIsNone(data) + self.assertIsNone(stream) + self.assertEquals(stream.rule.error, "Stream could not be parsed") + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called() + + def test_client_catches_long_exception_text(self): + timeline = TwitterTimelineFactory() + mock_stream = Mock(rule=timeline) + + self.mocked_read.side_effect = StreamParseException(message=words(1000)) + + with TwitterClient([timeline]) as client: + for data, stream in client: + self.assertIsNone(data) + self.assertIsNone(stream) + self.assertEquals(len(stream.rule.error), 1024) + self.assertEquals(stream.rule.succeeded, False) + + self.mocked_read.assert_called() diff --git a/src/newsreader/news/collection/tests/twitter/collector/__init__.py b/src/newsreader/news/collection/tests/twitter/collector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/twitter/collector/mocks.py b/src/newsreader/news/collection/tests/twitter/collector/mocks.py new file mode 100644 index 0000000..c57f9cf --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/collector/mocks.py @@ -0,0 +1,227 @@ +# retrieved with: +# curl -X GET -H "Authorization: Bearer " "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=RobertsSpaceInd&tweet_mode=extended" | python3 -m json.tool --sort-keys + +simple_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Sep 18 20:32:22 +0000 2020", + "display_text_range": [0, 111], + "entities": { + "hashtags": [{"indices": [26, 41], "text": "SCShipShowdown"}], + "symbols": [], + "urls": [], + "user_mentions": [], + }, + "favorite_count": 54, + "favorited": False, + "full_text": "It's a close match-up for #SCShipShowdown today! Which Aegis ship do you think will make it to the Semi-Finals?", + "geo": None, + "id": 1307054882210435074, + "id_str": "1307054882210435074", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "retweet_count": 9, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4831, + "follow_request_sent": None, + "followers_count": 106971, + "following": None, + "friends_count": 204, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 893, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6368, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Sep 18 18:50:11 +0000 2020", + "display_text_range": [0, 271], + "entities": { + "hashtags": [{"indices": [211, 218], "text": "Twitch"}], + "media": [ + { + "display_url": "pic.twitter.com/Cey5JpR1i9", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1307029168941461504/photo/1", + "id": 1307028141697765376, + "id_str": "1307028141697765376", + "indices": [272, 295], + "media_url": "http://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "media_url_https": "https://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "sizes": { + "large": {"h": 1090, "resize": "fit", "w": 1920}, + "medium": {"h": 681, "resize": "fit", "w": 1200}, + "small": {"h": 386, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/Cey5JpR1i9", + } + ], + "symbols": [], + "urls": [ + { + "display_url": "twitch.tv/starcitizen", + "expanded_url": "http://twitch.tv/starcitizen", + "indices": [248, 271], + "url": "https://t.co/2AdNovhpFW", + } + ], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "display_url": "pic.twitter.com/Cey5JpR1i9", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1307029168941461504/photo/1", + "id": 1307028141697765376, + "id_str": "1307028141697765376", + "indices": [272, 295], + "media_url": "http://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "media_url_https": "https://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "sizes": { + "large": {"h": 1090, "resize": "fit", "w": 1920}, + "medium": {"h": 681, "resize": "fit", "w": 1200}, + "small": {"h": 386, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/Cey5JpR1i9", + } + ] + }, + "favorite_count": 90, + "favorited": False, + "full_text": "We\u2019re welcoming members of our Builds, Publishes and Platform teams on Star Citizen Live to talk about the process involved in bringing everyone\u2019s work together and getting it out into your hands. Going live on #Twitch in 10 minutes. \ud83c\udfa5\ud83d\udd34 \n\nTune in: https://t.co/2AdNovhpFW https://t.co/Cey5JpR1i9", + "geo": None, + "id": 1307029168941461504, + "id_str": "1307029168941461504", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 13, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4831, + "follow_request_sent": None, + "followers_count": 106971, + "following": None, + "friends_count": 204, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 893, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6368, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] + +empty_mock = [] diff --git a/src/newsreader/news/collection/tests/twitter/collector/tests.py b/src/newsreader/news/collection/tests/twitter/collector/tests.py new file mode 100644 index 0000000..766e971 --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/collector/tests.py @@ -0,0 +1,180 @@ +from datetime import datetime +from unittest.mock import patch +from uuid import uuid4 + +from django.test import TestCase +from django.utils import timezone + +import pytz + +from freezegun import freeze_time +from ftfy import fix_text + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.exceptions import ( + StreamDeniedException, + StreamForbiddenException, + StreamNotFoundException, + StreamTimeOutException, +) +from newsreader.news.collection.tests.factories import TwitterTimelineFactory +from newsreader.news.collection.tests.twitter.collector.mocks import ( + empty_mock, + simple_mock, +) +from newsreader.news.collection.twitter import TWITTER_URL, TwitterCollector +from newsreader.news.collection.utils import truncate_text +from newsreader.news.core.models import Post + + +@freeze_time("2020-09-26 14:40:00") +class TwitterCollectorTestCase(TestCase): + def setUp(self): + patched_get = patch("newsreader.news.collection.twitter.fetch") + self.mocked_fetch = patched_get.start() + + patched_parse = patch("newsreader.news.collection.twitter.TwitterStream.parse") + self.mocked_parse = patched_parse.start() + + def tearDown(self): + patch.stopall() + + def test_simple_batch(self): + self.mocked_parse.return_value = simple_mock + + timeline = TwitterTimelineFactory( + user__twitter_oauth_token=str(uuid4()), + user__twitter_oauth_token_secret=str(uuid4()), + screen_name="RobertsSpaceInd", + enabled=True, + ) + + collector = TwitterCollector() + collector.collect(rules=[timeline]) + + self.assertCountEqual( + Post.objects.values_list("remote_identifier", flat=True), + ("1307054882210435074", "1307029168941461504"), + ) + + self.assertEquals(timeline.succeeded, True) + self.assertEquals(timeline.last_run, timezone.now()) + self.assertIsNone(timeline.error) + + post = Post.objects.get( + remote_identifier="1307054882210435074", + rule__type=RuleTypeChoices.twitter_timeline, + ) + + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 9, 18, 20, 32, 22)) + ) + + title = truncate_text( + Post, + "title", + "It's a close match-up for #SCShipShowdown today! Which Aegis ship" + " do you think will make it to the Semi-Finals?", + ) + + self.assertEquals(post.author, "RobertsSpaceInd") + self.assertEquals(post.title, title) + self.assertEquals( + post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1307054882210435074" + ) + + post = Post.objects.get( + remote_identifier="1307029168941461504", + rule__type=RuleTypeChoices.twitter_timeline, + ) + + self.assertEquals( + post.publication_date, pytz.utc.localize(datetime(2020, 9, 18, 18, 50, 11)) + ) + + body = fix_text( + "We\u2019re welcoming members of our Builds, Publishes and Platform" + " teams on Star Citizen Live to talk about the process involved in" + " bringing everyone\u2019s work together and getting it out into your" + " hands. Going live on #Twitch in 10 minutes." + " \ud83c\udfa5\ud83d\udd34 \n\nTune in:" + " https://t.co/2AdNovhpFW https://t.co/Cey5JpR1i9" + ) + + title = truncate_text(Post, "title", body) + + self.assertEquals(post.author, "RobertsSpaceInd") + self.assertEquals(post.title, title) + self.assertEquals( + post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1307029168941461504" + ) + + def test_empty_batch(self): + self.mocked_parse.return_value = empty_mock + + timeline = TwitterTimelineFactory() + + collector = TwitterCollector() + collector.collect(rules=[timeline]) + + self.assertEquals(Post.objects.count(), 0) + + self.assertEquals(timeline.succeeded, True) + self.assertEquals(timeline.last_run, timezone.now()) + self.assertIsNone(timeline.error) + + def test_not_found(self): + self.mocked_fetch.side_effect = StreamNotFoundException + + timeline = TwitterTimelineFactory() + + collector = TwitterCollector() + collector.collect(rules=[timeline]) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(timeline.succeeded, False) + self.assertEquals(timeline.error, "Stream not found") + + def test_denied(self): + self.mocked_fetch.side_effect = StreamDeniedException + + timeline = TwitterTimelineFactory( + user__twitter_oauth_token=str(uuid4()), + user__twitter_oauth_token_secret=str(uuid4()), + ) + + collector = TwitterCollector() + collector.collect(rules=[timeline]) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(timeline.succeeded, False) + self.assertEquals(timeline.error, "Stream does not have sufficient permissions") + + user = timeline.user + + self.assertIsNone(user.twitter_oauth_token) + self.assertIsNone(user.twitter_oauth_token_secret) + + def test_forbidden(self): + self.mocked_fetch.side_effect = StreamForbiddenException + + timeline = TwitterTimelineFactory() + + collector = TwitterCollector() + collector.collect(rules=[timeline]) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(timeline.succeeded, False) + self.assertEquals(timeline.error, "Stream forbidden") + + def test_timed_out(self): + self.mocked_fetch.side_effect = StreamTimeOutException + + timeline = TwitterTimelineFactory() + + collector = TwitterCollector() + collector.collect(rules=[timeline]) + + self.assertEquals(Post.objects.count(), 0) + self.assertEquals(timeline.succeeded, False) + self.assertEquals(timeline.error, "Stream timed out") diff --git a/src/newsreader/news/collection/tests/twitter/stream/__init__.py b/src/newsreader/news/collection/tests/twitter/stream/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/news/collection/tests/twitter/stream/mocks.py b/src/newsreader/news/collection/tests/twitter/stream/mocks.py new file mode 100644 index 0000000..1b7c6a2 --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/stream/mocks.py @@ -0,0 +1,225 @@ +# retrieved with: +# curl -X GET -H "Authorization: Bearer " "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=RobertsSpaceInd&tweet_mode=extended" | python3 -m json.tool --sort-keys + +simple_mock = [ + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Sep 18 20:32:22 +0000 2020", + "display_text_range": [0, 111], + "entities": { + "hashtags": [{"indices": [26, 41], "text": "SCShipShowdown"}], + "symbols": [], + "urls": [], + "user_mentions": [], + }, + "favorite_count": 54, + "favorited": False, + "full_text": "It's a close match-up for #SCShipShowdown today! Which Aegis ship do you think will make it to the Semi-Finals?", + "geo": None, + "id": 1307054882210435074, + "id_str": "1307054882210435074", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "retweet_count": 9, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4831, + "follow_request_sent": None, + "followers_count": 106971, + "following": None, + "friends_count": 204, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 893, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6368, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, + { + "contributors": None, + "coordinates": None, + "created_at": "Fri Sep 18 18:50:11 +0000 2020", + "display_text_range": [0, 271], + "entities": { + "hashtags": [{"indices": [211, 218], "text": "Twitch"}], + "media": [ + { + "display_url": "pic.twitter.com/Cey5JpR1i9", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1307029168941461504/photo/1", + "id": 1307028141697765376, + "id_str": "1307028141697765376", + "indices": [272, 295], + "media_url": "http://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "media_url_https": "https://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "sizes": { + "large": {"h": 1090, "resize": "fit", "w": 1920}, + "medium": {"h": 681, "resize": "fit", "w": 1200}, + "small": {"h": 386, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/Cey5JpR1i9", + } + ], + "symbols": [], + "urls": [ + { + "display_url": "twitch.tv/starcitizen", + "expanded_url": "http://twitch.tv/starcitizen", + "indices": [248, 271], + "url": "https://t.co/2AdNovhpFW", + } + ], + "user_mentions": [], + }, + "extended_entities": { + "media": [ + { + "display_url": "pic.twitter.com/Cey5JpR1i9", + "expanded_url": "https://twitter.com/RobertsSpaceInd/status/1307029168941461504/photo/1", + "id": 1307028141697765376, + "id_str": "1307028141697765376", + "indices": [272, 295], + "media_url": "http://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "media_url_https": "https://pbs.twimg.com/media/EiN_K4FVkAAGBcr.jpg", + "sizes": { + "large": {"h": 1090, "resize": "fit", "w": 1920}, + "medium": {"h": 681, "resize": "fit", "w": 1200}, + "small": {"h": 386, "resize": "fit", "w": 680}, + "thumb": {"h": 150, "resize": "crop", "w": 150}, + }, + "type": "photo", + "url": "https://t.co/Cey5JpR1i9", + } + ] + }, + "favorite_count": 90, + "favorited": False, + "full_text": "We\u2019re welcoming members of our Builds, Publishes and Platform teams on Star Citizen Live to talk about the process involved in bringing everyone\u2019s work together and getting it out into your hands. Going live on #Twitch in 10 minutes. \ud83c\udfa5\ud83d\udd34 \n\nTune in: https://t.co/2AdNovhpFW https://t.co/Cey5JpR1i9", + "geo": None, + "id": 1307029168941461504, + "id_str": "1307029168941461504", + "in_reply_to_screen_name": None, + "in_reply_to_status_id": None, + "in_reply_to_status_id_str": None, + "in_reply_to_user_id": None, + "in_reply_to_user_id_str": None, + "is_quote_status": False, + "lang": "en", + "place": None, + "possibly_sensitive": False, + "retweet_count": 13, + "retweeted": False, + "source": 'Twitter Web App', + "truncated": False, + "user": { + "contributors_enabled": False, + "created_at": "Wed Sep 05 00:58:11 +0000 2012", + "default_profile": False, + "default_profile_image": False, + "description": "The official Twitter profile for #StarCitizen and Roberts Space Industries.", + "entities": { + "description": {"urls": []}, + "url": { + "urls": [ + { + "display_url": "robertsspaceindustries.com", + "expanded_url": "http://www.robertsspaceindustries.com", + "indices": [0, 23], + "url": "https://t.co/iqO6apof3y", + } + ] + }, + }, + "favourites_count": 4831, + "follow_request_sent": None, + "followers_count": 106971, + "following": None, + "friends_count": 204, + "geo_enabled": False, + "has_extended_profile": False, + "id": 803542770, + "id_str": "803542770", + "is_translation_enabled": False, + "is_translator": False, + "lang": None, + "listed_count": 893, + "location": "Roberts Space Industries", + "name": "Star Citizen", + "notifications": None, + "profile_background_color": "131516", + "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", + "profile_background_tile": False, + "profile_banner_url": "https://pbs.twimg.com/profile_banners/803542770/1596651186", + "profile_image_url": "http://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/963109950103814144/ysnj_Asy_normal.jpg", + "profile_link_color": "0A5485", + "profile_sidebar_border_color": "FFFFFF", + "profile_sidebar_fill_color": "EFEFEF", + "profile_text_color": "333333", + "profile_use_background_image": True, + "protected": False, + "screen_name": "RobertsSpaceInd", + "statuses_count": 6368, + "time_zone": None, + "translator_type": "none", + "url": "https://t.co/iqO6apof3y", + "utc_offset": None, + "verified": True, + }, + }, +] diff --git a/src/newsreader/news/collection/tests/twitter/stream/tests.py b/src/newsreader/news/collection/tests/twitter/stream/tests.py new file mode 100644 index 0000000..4edb639 --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/stream/tests.py @@ -0,0 +1,107 @@ +from json import JSONDecodeError +from unittest.mock import patch + +from django.test import TestCase + +from newsreader.news.collection.exceptions import ( + StreamDeniedException, + StreamException, + StreamForbiddenException, + StreamNotFoundException, + StreamParseException, + StreamTimeOutException, + StreamTooManyException, +) +from newsreader.news.collection.tests.factories import TwitterTimelineFactory +from newsreader.news.collection.tests.twitter.stream.mocks import simple_mock +from newsreader.news.collection.twitter import TwitterStream + + +class TwitterStreamTestCase(TestCase): + def setUp(self): + self.patched_fetch = patch("newsreader.news.collection.twitter.fetch") + self.mocked_fetch = self.patched_fetch.start() + + def tearDown(self): + patch.stopall() + + def test_simple_stream(self): + self.mocked_fetch.return_value.json.return_value = simple_mock + + timeline = TwitterTimelineFactory() + stream = TwitterStream(timeline) + + data, stream = stream.read() + + self.assertEquals(data, simple_mock) + self.assertEquals(stream, stream) + + self.mocked_fetch.assert_called() + + def test_stream_raises_exception(self): + self.mocked_fetch.side_effect = StreamException + + timeline = TwitterTimelineFactory() + stream = TwitterStream(timeline) + + with self.assertRaises(StreamException): + stream.read() + + self.mocked_fetch.assert_called() + + def test_stream_raises_denied_exception(self): + self.mocked_fetch.side_effect = StreamDeniedException + + timeline = TwitterTimelineFactory() + stream = TwitterStream(timeline) + + with self.assertRaises(StreamDeniedException): + stream.read() + + self.mocked_fetch.assert_called() + + def test_stream_raises_not_found_exception(self): + self.mocked_fetch.side_effect = StreamNotFoundException + + timeline = TwitterTimelineFactory() + stream = TwitterStream(timeline) + + with self.assertRaises(StreamNotFoundException): + stream.read() + + self.mocked_fetch.assert_called() + + def test_stream_raises_time_out_exception(self): + self.mocked_fetch.side_effect = StreamTimeOutException + + timeline = TwitterTimelineFactory() + stream = TwitterStream(timeline) + + with self.assertRaises(StreamTimeOutException): + stream.read() + + self.mocked_fetch.assert_called() + + def test_stream_raises_forbidden_exception(self): + self.mocked_fetch.side_effect = StreamForbiddenException + + timeline = TwitterTimelineFactory() + stream = TwitterStream(timeline) + + with self.assertRaises(StreamForbiddenException): + stream.read() + + self.mocked_fetch.assert_called() + + def test_stream_raises_parse_exception(self): + self.mocked_fetch.return_value.json.side_effect = JSONDecodeError( + "No json found", "{}", 5 + ) + + timeline = TwitterTimelineFactory() + stream = TwitterStream(timeline) + + with self.assertRaises(StreamParseException): + stream.read() + + self.mocked_fetch.assert_called() diff --git a/src/newsreader/news/collection/tests/twitter/test_scheduler.py b/src/newsreader/news/collection/tests/twitter/test_scheduler.py new file mode 100644 index 0000000..a3c2db8 --- /dev/null +++ b/src/newsreader/news/collection/tests/twitter/test_scheduler.py @@ -0,0 +1,63 @@ +from json import JSONDecodeError +from unittest.mock import patch + +from django.test import TestCase + +from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.exceptions import StreamException +from newsreader.news.collection.twitter import TwitterTimeLineScheduler + + +class TwitterTimeLineSchedulerTestCase(TestCase): + def setUp(self): + patched_fetch = patch("newsreader.news.collection.twitter.fetch") + self.mocked_fetch = patched_fetch.start() + + def test_simple(self): + user = UserFactory(twitter_oauth_token="foo", twitter_oauth_token_secret="bar") + + self.mocked_fetch.return_value.json.return_value = { + "rate_limit_context": {"application": "dummykey"}, + "resources": { + "statuses": { + "/statuses/user_timeline": { + "limit": 1500, + "remaining": 1500, + "reset": 1601141386, + } + } + }, + } + + scheduler = TwitterTimeLineScheduler(user) + + self.assertEquals(scheduler.get_current_ratelimit(), 1500) + + def test_stream_exception(self): + user = UserFactory(twitter_oauth_token=None, twitter_oauth_token_secret=None) + + self.mocked_fetch.side_effect = StreamException + + scheduler = TwitterTimeLineScheduler(user) + + self.assertEquals(scheduler.get_current_ratelimit(), None) + + def test_json_decode_error(self): + user = UserFactory(twitter_oauth_token="foo", twitter_oauth_token_secret="bar") + + self.mocked_fetch.return_value.json.side_effect = JSONDecodeError( + "foo", "bar", 10 + ) + + scheduler = TwitterTimeLineScheduler(user) + + self.assertEquals(scheduler.get_current_ratelimit(), None) + + def test_unexpected_contents(self): + user = UserFactory(twitter_oauth_token="foo", twitter_oauth_token_secret="bar") + + self.mocked_fetch.return_value.json.return_value = {"foo": "bar"} + + scheduler = TwitterTimeLineScheduler(user) + + self.assertEquals(scheduler.get_current_ratelimit(), None) diff --git a/src/newsreader/news/collection/tests/views/base.py b/src/newsreader/news/collection/tests/views/base.py index a651719..17f232c 100644 --- a/src/newsreader/news/collection/tests/views/base.py +++ b/src/newsreader/news/collection/tests/views/base.py @@ -46,6 +46,7 @@ class CollectionRuleViewTestCase: name="new name", category=other_rule.category, url=other_rule.url, + timezone=other_rule.timezone, ) other_url = reverse("news:collection:feed-update", args=[other_rule.pk]) diff --git a/src/newsreader/news/collection/tests/views/test_crud.py b/src/newsreader/news/collection/tests/views/test_crud.py index d4bd731..7da241d 100644 --- a/src/newsreader/news/collection/tests/views/test_crud.py +++ b/src/newsreader/news/collection/tests/views/test_crud.py @@ -1,6 +1,8 @@ from django.test import TestCase from django.urls import reverse +import pytz + from django_celery_beat.models import PeriodicTask from newsreader.news.collection.choices import RuleTypeChoices @@ -19,21 +21,23 @@ class FeedCreateViewTestCase(CollectionRuleViewTestCase, TestCase): self.form_data.update( name="new rule", url="https://www.rss.com/rss", + timezone=pytz.utc, category=str(self.category.pk), ) def test_creation(self): response = self.client.post(self.url, self.form_data) - self.assertEqual(response.status_code, 302) + self.assertEquals(response.status_code, 302) rule = CollectionRule.objects.get(name="new rule") - self.assertEqual(rule.type, RuleTypeChoices.feed) - self.assertEqual(rule.url, "https://www.rss.com/rss") - self.assertEqual(rule.favicon, None) - self.assertEqual(rule.category.pk, self.category.pk) - self.assertEqual(rule.user.pk, self.user.pk) + self.assertEquals(rule.type, RuleTypeChoices.feed) + self.assertEquals(rule.url, "https://www.rss.com/rss") + self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.favicon, None) + self.assertEquals(rule.category.pk, self.category.pk) + self.assertEquals(rule.user.pk, self.user.pk) self.assertTrue( PeriodicTask.objects.get( @@ -55,17 +59,18 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): name=self.rule.name, category=self.rule.category.pk, url=self.rule.url, + timezone=self.rule.timezone, ) def test_name_change(self): self.form_data.update(name="new name") response = self.client.post(self.url, self.form_data) - self.assertEqual(response.status_code, 302) + self.assertEquals(response.status_code, 302) self.rule.refresh_from_db() - self.assertEqual(self.rule.name, "new name") + self.assertEquals(self.rule.name, "new name") def test_category_change(self): new_category = CategoryFactory(user=self.user) @@ -73,18 +78,32 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): self.form_data.update(category=new_category.pk) response = self.client.post(self.url, self.form_data) - self.assertEqual(response.status_code, 302) + self.assertEquals(response.status_code, 302) self.rule.refresh_from_db() - self.assertEqual(self.rule.category.pk, new_category.pk) + self.assertEquals(self.rule.category.pk, new_category.pk) def test_category_removal(self): self.form_data.update(category="") response = self.client.post(self.url, self.form_data) - self.assertEqual(response.status_code, 302) + self.assertEquals(response.status_code, 302) self.rule.refresh_from_db() - self.assertEqual(self.rule.category, None) + self.assertEquals(self.rule.category, None) + + def test_rules_only(self): + rule = FeedFactory( + name="Python", + url="https://reddit.com/r/python", + user=self.user, + category=self.category, + type=RuleTypeChoices.subreddit, + ) + url = reverse("news:collection:feed-update", kwargs={"pk": rule.pk}) + + response = self.client.get(url) + + self.assertEquals(response.status_code, 404) diff --git a/src/newsreader/news/collection/tests/views/test_subreddit_views.py b/src/newsreader/news/collection/tests/views/test_subreddit_views.py new file mode 100644 index 0000000..a644800 --- /dev/null +++ b/src/newsreader/news/collection/tests/views/test_subreddit_views.py @@ -0,0 +1,138 @@ +from django.test import TestCase +from django.urls import reverse +from django.utils.translation import gettext as _ + +import pytz + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL +from newsreader.news.collection.tests.factories import SubredditFactory +from newsreader.news.collection.tests.views.base import CollectionRuleViewTestCase +from newsreader.news.core.tests.factories import CategoryFactory + + +class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase): + def setUp(self): + super().setUp() + + self.form_data = { + "name": "new rule", + "url": f"{REDDIT_API_URL}/r/aww", + "category": str(self.category.pk), + "reddit_allow_nfsw": False, + "reddit_allow_spoiler": False, + "reddit_allow_viewed": True, + "reddit_upvotes_min": 0, + "reddit_comments_min": 0, + } + + self.url = reverse("news:collection:subreddit-create") + + def test_creation(self): + response = self.client.post(self.url, self.form_data) + + self.assertEquals(response.status_code, 302) + + rule = CollectionRule.objects.get(name="new rule") + + self.assertEquals(rule.type, RuleTypeChoices.subreddit) + self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") + self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.favicon, None) + self.assertEquals(rule.category.pk, self.category.pk) + self.assertEquals(rule.user.pk, self.user.pk) + + def test_regular_reddit_url(self): + self.form_data.update(url=f"{REDDIT_URL}/r/aww") + + response = self.client.post(self.url, self.form_data) + + self.assertContains(response, _("This does not look like an Reddit API URL")) + + +class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): + def setUp(self): + super().setUp() + + self.rule = SubredditFactory( + name="Python", + url=f"{REDDIT_API_URL}/r/python.json", + user=self.user, + category=self.category, + type=RuleTypeChoices.subreddit, + ) + self.url = reverse( + "news:collection:subreddit-update", kwargs={"pk": self.rule.pk} + ) + + self.form_data = { + "name": self.rule.name, + "url": self.rule.url, + "category": str(self.category.pk), + "timezone": pytz.utc, + "reddit_allow_nfsw": False, + "reddit_allow_spoiler": False, + "reddit_allow_viewed": True, + "reddit_upvotes_min": 0, + "reddit_comments_min": 0, + } + + def test_name_change(self): + self.form_data.update(name="Python 2") + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.name, "Python 2") + + def test_category_change(self): + new_category = CategoryFactory(user=self.user) + + self.form_data.update(category=new_category.pk) + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.category.pk, new_category.pk) + + def test_subreddit_rules_only(self): + rule = SubredditFactory( + name="Fake subreddit", + url="https://leddit.com/r/python", + user=self.user, + category=self.category, + type=RuleTypeChoices.feed, + ) + url = reverse("news:collection:subreddit-update", kwargs={"pk": rule.pk}) + + response = self.client.get(url) + + self.assertEquals(response.status_code, 404) + + def test_url_change(self): + self.form_data.update(name="aww", url=f"{REDDIT_API_URL}/r/aww") + + response = self.client.post(self.url, self.form_data) + + self.assertEquals(response.status_code, 302) + + rule = CollectionRule.objects.get(name="aww") + + self.assertEquals(rule.type, RuleTypeChoices.subreddit) + self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") + self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.favicon, None) + self.assertEquals(rule.category.pk, self.category.pk) + self.assertEquals(rule.user.pk, self.user.pk) + + def test_regular_reddit_url(self): + self.form_data.update(url=f"{REDDIT_URL}/r/aww") + + response = self.client.post(self.url, self.form_data) + + self.assertContains(response, _("This does not look like an Reddit API URL")) diff --git a/src/newsreader/news/collection/tests/views/test_twitter_views.py b/src/newsreader/news/collection/tests/views/test_twitter_views.py new file mode 100644 index 0000000..d9afa26 --- /dev/null +++ b/src/newsreader/news/collection/tests/views/test_twitter_views.py @@ -0,0 +1,129 @@ +from django.test import TestCase +from django.urls import reverse + +import pytz + +from django_celery_beat.models import PeriodicTask + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.tests.factories import TwitterTimelineFactory +from newsreader.news.collection.tests.views.base import CollectionRuleViewTestCase +from newsreader.news.collection.twitter import TWITTER_API_URL +from newsreader.news.core.tests.factories import CategoryFactory + + +class TwitterTimelineCreateViewTestCase(CollectionRuleViewTestCase, TestCase): + def setUp(self): + super().setUp() + + self.form_data = { + "name": "new rule", + "screen_name": "RobertsSpaceInd", + "category": str(self.category.pk), + } + + self.url = reverse("news:collection:twitter-timeline-create") + + def test_creation(self): + response = self.client.post(self.url, self.form_data) + + self.assertEquals(response.status_code, 302) + + rule = CollectionRule.objects.get(name="new rule") + + self.assertEquals(rule.type, RuleTypeChoices.twitter_timeline) + self.assertEquals( + rule.url, + f"{TWITTER_API_URL}/statuses/user_timeline.json?screen_name=RobertsSpaceInd&tweet_mode=extended", + ) + self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.favicon, None) + self.assertEquals(rule.category.pk, self.category.pk) + self.assertEquals(rule.user.pk, self.user.pk) + + self.assertTrue( + PeriodicTask.objects.get( + name=f"{self.user.email}-timeline", + task="TwitterTimelineTask", + enabled=True, + ) + ) + + +class TwitterTimelineUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): + def setUp(self): + super().setUp() + + self.rule = TwitterTimelineFactory( + name="Star citizen", + screen_name="RobertsSpaceInd", + user=self.user, + category=self.category, + type=RuleTypeChoices.twitter_timeline, + ) + self.url = reverse( + "news:collection:twitter-timeline-update", kwargs={"pk": self.rule.pk} + ) + + self.form_data = { + "name": self.rule.name, + "screen_name": self.rule.screen_name, + "category": str(self.category.pk), + "timezone": pytz.utc, + } + + def test_name_change(self): + self.form_data.update(name="Star citizen Twitter") + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.name, "Star citizen Twitter") + + def test_category_change(self): + new_category = CategoryFactory(user=self.user) + + self.form_data.update(category=new_category.pk) + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.category.pk, new_category.pk) + + def test_twitter_timelines_only(self): + rule = TwitterTimelineFactory( + name="Fake twitter", + user=self.user, + category=self.category, + type=RuleTypeChoices.feed, + url="https://twitter.com/RobertsSpaceInd", + ) + url = reverse("news:collection:twitter-timeline-update", kwargs={"pk": rule.pk}) + + response = self.client.get(url) + + self.assertEquals(response.status_code, 404) + + def test_screen_name_change(self): + self.form_data.update(screen_name="CyberpunkGame") + + response = self.client.post(self.url, self.form_data) + + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.type, RuleTypeChoices.twitter_timeline) + self.assertEquals( + self.rule.url, + f"{TWITTER_API_URL}/statuses/user_timeline.json?screen_name=CyberpunkGame&tweet_mode=extended", + ) + self.assertEquals(self.rule.timezone, str(pytz.utc)) + self.assertEquals(self.rule.favicon, None) + self.assertEquals(self.rule.category.pk, self.category.pk) + self.assertEquals(self.rule.user.pk, self.user.pk) diff --git a/src/newsreader/news/collection/twitter.py b/src/newsreader/news/collection/twitter.py new file mode 100644 index 0000000..2c488d1 --- /dev/null +++ b/src/newsreader/news/collection/twitter.py @@ -0,0 +1,343 @@ +import logging + +from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import datetime +from json import JSONDecodeError + +from django.conf import settings +from django.core.mail import send_mail +from django.utils import timezone +from django.utils.html import format_html, urlize +from django.utils.translation import ugettext as _ + +import pytz + +from ftfy import fix_text +from requests_oauthlib import OAuth1 as OAuth + +from newsreader.news.collection.base import ( + PostBuilder, + PostClient, + PostCollector, + PostStream, + Scheduler, +) +from newsreader.news.collection.choices import RuleTypeChoices, TwitterPostTypeChoices +from newsreader.news.collection.exceptions import ( + BuilderDuplicateException, + BuilderException, + BuilderMissingDataException, + BuilderParseException, + StreamDeniedException, + StreamException, + StreamNotFoundException, + StreamParseException, + StreamTimeOutException, + StreamTooManyException, +) +from newsreader.news.collection.utils import fetch, truncate_text +from newsreader.news.core.models import Post + + +logger = logging.getLogger(__name__) + +TWITTER_URL = "https://twitter.com" +TWITTER_API_URL = "https://api.twitter.com/1.1" +TWITTER_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token" +TWITTER_AUTH_URL = "https://api.twitter.com/oauth/authorize" +TWITTER_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token" +TWITTER_REVOKE_URL = f"{TWITTER_API_URL}/oauth/invalidate_token" + + +class TwitterBuilder(PostBuilder): + rule_type = RuleTypeChoices.twitter_timeline + + def build(self): + results = {} + + for post in self.payload: + try: + post = self.build_post(post) + except BuilderDuplicateException: + logger.warning("Skipping duplicate post") + continue + except BuilderException: + logger.exception("Failed building post") + continue + + identifier = post.remote_identifier + results[identifier] = post + + self.instances = results.values() + + def build_post(self, data): + remote_identifier = data.get("id_str", "") + rule = self.stream.rule + + if remote_identifier in self.existing_posts: + raise BuilderDuplicateException(payload=data) + + try: + body = urlize(data["full_text"], nofollow=True) + title = truncate_text( + Post, "title", self.sanitize_fragment(data["full_text"]) + ) + + publication_date = pytz.utc.localize( + datetime.strptime(data["created_at"], "%a %b %d %H:%M:%S +0000 %Y") + ) + except KeyError as e: + raise BuilderMissingDataException(payload=data) from e + except (OverflowError, OSError) as e: + raise BuilderParseException(payload=data) from e + + url = f"{TWITTER_URL}/{rule.screen_name}/status/{remote_identifier}" + + if "extended_entities" in data: + try: + media_entities = self.get_media_entities(data) + body += media_entities + except KeyError as e: + raise BuilderMissingDataException( + message="Failed parsing data for media entities", payload=data + ) from e + + try: + if "retweeted_status" in data: + original_post = data["retweeted_status"] + original_tweet = urlize(original_post["full_text"], nofollow=True) + body = f"{body}
    Original tweet: {original_tweet}
    " + if "quoted_status" in data: + original_post = data["quoted_status"] + original_tweet = urlize(original_post["full_text"], nofollow=True) + body = f"{body}
    Quoted tweet: {original_tweet}
    " + except KeyError as e: + raise BuilderMissingDataException( + message="Failed parsing data for original tweet", payload=data + ) from e + + body = self.sanitize_fragment(body) + + return Post( + **{ + "remote_identifier": remote_identifier, + "title": fix_text(title), + "body": fix_text(body), + "author": rule.screen_name, + "publication_date": publication_date, + "url": url, + "rule": rule, + } + ) + + def get_media_entities(self, data): + media_entities = data["extended_entities"]["media"] + formatted_entities = "" + + for media_entity in media_entities: + media_type = media_entity["type"] + media_url = media_entity["media_url_https"] + title = media_entity["id_str"] + + if media_type == TwitterPostTypeChoices.photo: + html_fragment = format_html( + """
    {title}
    """, + title=title, + media_url=media_url, + ) + + formatted_entities += html_fragment + + elif media_type in ( + TwitterPostTypeChoices.video, + TwitterPostTypeChoices.animated_gif, + ): + meta_data = media_entity["video_info"] + + videos = sorted( + [video for video in meta_data["variants"]], + reverse=True, + key=lambda video: video.get("bitrate", 0), + ) + + if not videos: + continue + + video = videos[0] + content_type = video["content_type"] + url = video["url"] + + html_fragment = format_html( + """
    """, + url=url, + content_type=content_type, + ) + + formatted_entities += html_fragment + + return formatted_entities + + def __str__(self): + return f"{self.stream.rule.pk}: TwitterBuilder" + + +class TwitterStream(PostStream): + rule_type = RuleTypeChoices.twitter_timeline + + def read(self): + oauth = OAuth( + settings.TWITTER_CONSUMER_ID, + client_secret=settings.TWITTER_CONSUMER_SECRET, + resource_owner_key=self.rule.user.twitter_oauth_token, + resource_owner_secret=self.rule.user.twitter_oauth_token_secret, + ) + + response = fetch(self.rule.url, auth=oauth) + + return self.parse(response), self + + def parse(self, response): + try: + return response.json() + except JSONDecodeError as e: + raise StreamParseException( + response=response, message="Failed parsing json" + ) from e + + def __str__(self): + return f"{self.rule.pk}: TwitterStream" + + +class TwitterClient(PostClient): + stream = TwitterStream + + def __enter__(self): + streams = [self.stream(timeline) for timeline in self.rules] + + with ThreadPoolExecutor(max_workers=10) as executor: + futures = {executor.submit(stream.read): stream for stream in streams} + + for future in as_completed(futures): + stream = futures[future] + + try: + payload = future.result() + + stream.rule.error = None + stream.rule.succeeded = True + + yield payload + except StreamTooManyException as e: + logger.exception("Ratelimit hit, aborting twitter calls") + + self.set_rule_error(stream.rule, e) + + break + except StreamDeniedException as e: + logger.warning( + f"Access token expired for user {stream.rule.user.pk}" + ) + + try: + import sentry_sdk + + with sentry_sdk.push_scope() as scope: + scope.set_extra("content", e.response.content) + sentry_sdk.capture_message( + "Twitter authentication credentials reset" + ) + except ImportError: + pass + + stream.rule.user.twitter_oauth_token = None + stream.rule.user.twitter_oauth_token_secret = None + stream.rule.user.save() + + message = _( + "Your Twitter account credentials have expired. Re-authenticate in" + " the settings page to keep retrieving Twitter specific information" + " from your account." + ) + + send_mail( + "Twitter account needs re-authentication", + message, + None, + [stream.rule.user.email], + ) + + self.set_rule_error(stream.rule, e) + + break + except (StreamNotFoundException, StreamTimeOutException) as e: + logger.warning(f"Request failed for {stream.rule.screen_name}") + + self.set_rule_error(stream.rule, e) + + continue + except StreamException as e: + logger.exception(f"Request failed for {stream.rule.screen_name}") + + self.set_rule_error(stream.rule, e) + + continue + finally: + stream.rule.last_run = timezone.now() + stream.rule.save() + + +class TwitterCollector(PostCollector): + builder = TwitterBuilder + client = TwitterClient + + +# see https://developer.twitter.com/en/docs/twitter-api/v1/rate-limits +class TwitterTimeLineScheduler(Scheduler): + def __init__(self, user, timelines=[]): + self.user = user + + if not timelines: + self.timelines = ( + user.rules.enabled() + .filter(type=RuleTypeChoices.twitter_timeline) + .order_by("last_run")[:200] + ) + else: + self.timelines = timelines + + def get_scheduled_rules(self): + max_amount = self.get_current_ratelimit() + return self.timelines[:max_amount] if max_amount else [] + + def get_current_ratelimit(self): + endpoint = "application/rate_limit_status.json?resources=statuses" + + if ( + not self.user.twitter_oauth_token + or not self.user.twitter_oauth_token_secret + ): + return + + oauth = OAuth( + settings.TWITTER_CONSUMER_ID, + client_secret=settings.TWITTER_CONSUMER_SECRET, + resource_owner_key=self.user.twitter_oauth_token, + resource_owner_secret=self.user.twitter_oauth_token_secret, + ) + + try: + response = fetch(f"{TWITTER_API_URL}/{endpoint}", auth=oauth) + except StreamException: + logger.exception(f"Unable to retrieve current ratelimit for {self.user.pk}") + return + + try: + payload = response.json() + except JSONDecodeError: + logger.exception(f"Unable to parse ratelimit request for {self.user.pk}") + return + + try: + return payload["resources"]["statuses"]["/statuses/user_timeline"]["limit"] + except KeyError: + return diff --git a/src/newsreader/news/collection/urls.py b/src/newsreader/news/collection/urls.py index a57a00e..e5276cb 100644 --- a/src/newsreader/news/collection/urls.py +++ b/src/newsreader/news/collection/urls.py @@ -14,6 +14,10 @@ from newsreader.news.collection.views import ( FeedCreateView, FeedUpdateView, OPMLImportView, + SubRedditCreateView, + SubRedditUpdateView, + TwitterTimelineCreateView, + TwitterTimelineUpdateView, ) @@ -47,4 +51,26 @@ urlpatterns = [ name="rules-disable", ), path("rules/import/", login_required(OPMLImportView.as_view()), name="import"), + # Reddit + path( + "subreddits/create/", + login_required(SubRedditCreateView.as_view()), + name="subreddit-create", + ), + path( + "subreddits//", + login_required(SubRedditUpdateView.as_view()), + name="subreddit-update", + ), + # Twitter + path( + "twitter/timelines/create/", + login_required(TwitterTimelineCreateView.as_view()), + name="twitter-timeline-create", + ), + path( + "twitter/timelines//", + login_required(TwitterTimelineUpdateView.as_view()), + name="twitter-timeline-update", + ), ] diff --git a/src/newsreader/news/collection/utils.py b/src/newsreader/news/collection/utils.py index 36a3b9e..0eb1dc0 100644 --- a/src/newsreader/news/collection/utils.py +++ b/src/newsreader/news/collection/utils.py @@ -1,8 +1,10 @@ -from datetime import datetime, timezone +from datetime import datetime from django.conf import settings from django.db.models.fields import CharField, TextField +from django.utils import timezone +import pytz import requests from requests.exceptions import RequestException @@ -13,11 +15,14 @@ from newsreader.news.collection.response_handler import ResponseHandler DEFAULT_HEADERS = {"User-Agent": f"linux:rss.fudiggity.nl:{settings.VERSION}"} -def build_publication_date(_datetime_info: tuple) -> datetime: +def build_publication_date(dt, tz): try: - return datetime(*_datetime_info[:6], tzinfo=timezone.utc) + naive_datetime = datetime(*dt[:6]) + published_parsed = timezone.make_aware(naive_datetime, timezone=tz) except (TypeError, ValueError): - return datetime.now(tz=timezone.utc) + return timezone.now() + + return published_parsed.astimezone(pytz.utc) def fetch(url, auth=None, headers={}): @@ -60,6 +65,6 @@ def truncate_text(cls, field_name, value): return value if len(value) > max_length: - return f"{value[: max_length - 1]}…" + return f"{value[:max_length - 1]}…" return value diff --git a/src/newsreader/news/collection/views/__init__.py b/src/newsreader/news/collection/views/__init__.py index dc92557..c66c5a5 100644 --- a/src/newsreader/news/collection/views/__init__.py +++ b/src/newsreader/news/collection/views/__init__.py @@ -3,20 +3,17 @@ from newsreader.news.collection.views.feed import ( FeedUpdateView, OPMLImportView, ) +from newsreader.news.collection.views.reddit import ( + SubRedditCreateView, + SubRedditUpdateView, +) from newsreader.news.collection.views.rules import ( CollectionRuleBulkDeleteView, CollectionRuleBulkDisableView, CollectionRuleBulkEnableView, CollectionRuleListView, ) - - -__all__ = [ - "FeedCreateView", - "FeedUpdateView", - "OPMLImportView", - "CollectionRuleBulkDeleteView", - "CollectionRuleBulkDisableView", - "CollectionRuleBulkEnableView", - "CollectionRuleListView", -] +from newsreader.news.collection.views.twitter import ( + TwitterTimelineCreateView, + TwitterTimelineUpdateView, +) diff --git a/src/newsreader/news/collection/views/base.py b/src/newsreader/news/collection/views/base.py index afce363..d7a3a4d 100644 --- a/src/newsreader/news/collection/views/base.py +++ b/src/newsreader/news/collection/views/base.py @@ -1,17 +1,16 @@ import json -from zoneinfo import available_timezones - from django.urls import reverse_lazy +import pytz + from django_celery_beat.models import IntervalSchedule, PeriodicTask from newsreader.news.collection.models import CollectionRule from newsreader.news.core.models import Category -from newsreader.utils.views import NavListMixin -class CollectionRuleViewMixin(NavListMixin): +class CollectionRuleViewMixin: queryset = CollectionRule.objects.order_by("name") def get_queryset(self): @@ -26,7 +25,7 @@ class CollectionRuleDetailMixin: context_data = super().get_context_data(**kwargs) categories = Category.objects.filter(user=self.request.user).order_by("name") - timezones = available_timezones() + timezones = [timezone for timezone in pytz.all_timezones] context_data["categories"] = categories context_data["timezones"] = timezones diff --git a/src/newsreader/news/collection/views/feed.py b/src/newsreader/news/collection/views/feed.py index 88c80e7..b7803d2 100644 --- a/src/newsreader/news/collection/views/feed.py +++ b/src/newsreader/news/collection/views/feed.py @@ -6,7 +6,11 @@ from django.views.generic.edit import CreateView, FormView, UpdateView from django_celery_beat.models import IntervalSchedule from newsreader.news.collection.choices import RuleTypeChoices -from newsreader.news.collection.forms import FeedForm, OPMLImportForm +from newsreader.news.collection.forms import ( + CollectionRuleBulkForm, + FeedForm, + OPMLImportForm, +) from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.views.base import ( CollectionRuleDetailMixin, @@ -14,7 +18,6 @@ from newsreader.news.collection.views.base import ( TaskCreationMixin, ) from newsreader.utils.opml import parse_opml -from newsreader.utils.views import NavListMixin class FeedUpdateView(CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView): @@ -37,7 +40,7 @@ class FeedCreateView( form_class = FeedForm -class OPMLImportView(NavListMixin, FormView): +class OPMLImportView(FormView): form_class = OPMLImportForm template_name = "news/collection/views/import.html" diff --git a/src/newsreader/news/collection/views/reddit.py b/src/newsreader/news/collection/views/reddit.py new file mode 100644 index 0000000..4e44e3f --- /dev/null +++ b/src/newsreader/news/collection/views/reddit.py @@ -0,0 +1,27 @@ +from django.views.generic.edit import CreateView, UpdateView + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.forms import SubRedditForm +from newsreader.news.collection.views.base import ( + CollectionRuleDetailMixin, + CollectionRuleViewMixin, +) + + +class SubRedditCreateView( + CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView +): + form_class = SubRedditForm + template_name = "news/collection/views/subreddit-create.html" + + +class SubRedditUpdateView( + CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView +): + form_class = SubRedditForm + template_name = "news/collection/views/subreddit-update.html" + context_object_name = "subreddit" + + def get_queryset(self): + queryset = super().get_queryset() + return queryset.filter(type=RuleTypeChoices.subreddit) diff --git a/src/newsreader/news/collection/views/rules.py b/src/newsreader/news/collection/views/rules.py index 753d9d7..902eedf 100644 --- a/src/newsreader/news/collection/views/rules.py +++ b/src/newsreader/news/collection/views/rules.py @@ -6,17 +6,19 @@ from django.views.generic.edit import FormView from django.views.generic.list import ListView from newsreader.news.collection.forms import CollectionRuleBulkForm -from newsreader.news.collection.views.base import CollectionRuleViewMixin -from newsreader.utils.views import NavListMixin +from newsreader.news.collection.views.base import ( + CollectionRuleDetailMixin, + CollectionRuleViewMixin, +) -class CollectionRuleListView(CollectionRuleViewMixin, NavListMixin, ListView): +class CollectionRuleListView(CollectionRuleViewMixin, ListView): paginate_by = 50 template_name = "news/collection/views/rules.html" context_object_name = "rules" -class CollectionRuleBulkView(NavListMixin, FormView): +class CollectionRuleBulkView(FormView): form_class = CollectionRuleBulkForm def get_redirect_url(self): diff --git a/src/newsreader/news/collection/views/twitter.py b/src/newsreader/news/collection/views/twitter.py new file mode 100644 index 0000000..0221a75 --- /dev/null +++ b/src/newsreader/news/collection/views/twitter.py @@ -0,0 +1,33 @@ +from django.views.generic.edit import CreateView, UpdateView + +from django_celery_beat.models import IntervalSchedule + +from newsreader.news.collection.choices import RuleTypeChoices +from newsreader.news.collection.forms import TwitterTimelineForm +from newsreader.news.collection.views.base import ( + CollectionRuleDetailMixin, + CollectionRuleViewMixin, + TaskCreationMixin, +) + + +class TwitterTimelineCreateView( + CollectionRuleViewMixin, CollectionRuleDetailMixin, TaskCreationMixin, CreateView +): + form_class = TwitterTimelineForm + template_name = "news/collection/views/twitter/timeline-create.html" + task_interval = (10, IntervalSchedule.MINUTES) + task_name = "timeline" + task_type = "TwitterTimelineTask" + + +class TwitterTimelineUpdateView( + CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView +): + form_class = TwitterTimelineForm + template_name = "news/collection/views/twitter/timeline-update.html" + context_object_name = "timeline" + + def get_queryset(self): + queryset = super().get_queryset() + return queryset.filter(type=RuleTypeChoices.twitter_timeline) diff --git a/src/newsreader/news/core/endpoints.py b/src/newsreader/news/core/endpoints.py index 092ec6f..b224024 100644 --- a/src/newsreader/news/core/endpoints.py +++ b/src/newsreader/news/core/endpoints.py @@ -1,9 +1,8 @@ -from django.db.models import Prefetch - from rest_framework import status from rest_framework.generics import ( GenericAPIView, ListAPIView, + ListCreateAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, get_object_or_404, @@ -13,19 +12,18 @@ from rest_framework.response import Response from newsreader.accounts.permissions import IsPostOwner from newsreader.core.pagination import CursorPagination -from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.serializers import RuleSerializer -from newsreader.news.core.filters import SavedFilter +from newsreader.news.core.filters import ReadFilter, SavedFilter from newsreader.news.core.models import Category, Post from newsreader.news.core.serializers import CategorySerializer, PostSerializer class ListPostView(ListAPIView): - queryset = Post.objects.filter(read=False) + queryset = Post.objects.all() serializer_class = PostSerializer permission_classes = (IsAuthenticated, IsPostOwner) pagination_class = CursorPagination - filter_backends = [SavedFilter] + filter_backends = [ReadFilter, SavedFilter] class DetailPostView(RetrieveUpdateAPIView): @@ -66,8 +64,10 @@ class NestedRuleCategoryView(ListAPIView): class NestedPostCategoryView(ListAPIView): + queryset = Category.objects.prefetch_related("rules", "rules__posts").all() serializer_class = PostSerializer pagination_class = CursorPagination + filter_backends = [ReadFilter] def get_queryset(self): lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field @@ -76,16 +76,13 @@ class NestedPostCategoryView(ListAPIView): # filtered on the user. filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} - rules_queryset = CollectionRule.objects.filter(user=self.request.user) - prefetch = Prefetch("rules", queryset=rules_queryset, to_attr="user_rules") - category_queryset = Category.objects.prefetch_related(prefetch).filter( - user=self.request.user - ) - category = get_object_or_404(category_queryset, **filter_kwargs) - + category = get_object_or_404(self.queryset, **filter_kwargs) self.check_object_permissions(self.request, category) - return Post.objects.filter(rule__in=category.user_rules, read=False) + rules = category.rules.values_list("id", flat=True) + queryset = Post.objects.filter(rule__in=rules) + + return queryset class CategoryReadView(GenericAPIView): diff --git a/src/newsreader/news/core/filters.py b/src/newsreader/news/core/filters.py index 9883304..ba3ea48 100644 --- a/src/newsreader/news/core/filters.py +++ b/src/newsreader/news/core/filters.py @@ -1,9 +1,37 @@ -from django.utils.translation import gettext_lazy as _ +from django.utils.encoding import force_text +from django.utils.translation import ugettext_lazy as _ from rest_framework import filters from rest_framework.compat import coreapi, coreschema +class ReadFilter(filters.BaseFilterBackend): + query_param = "read" + + def filter_queryset(self, request, queryset, view): + key = request.query_params.get(self.query_param, None) + available_values = {"True": True, "true": True, "False": False, "false": False} + + if not key or key not in available_values.keys(): + return queryset + + value = available_values[key] + return queryset.filter(read=value) + + def get_schema_fields(self, view): + return [ + coreapi.Field( + name=self.query_param, + required=False, + location="query", + schema=coreschema.String( + title=force_text(self.query_param), + description=force_text(_("Wether posts should be read or not")), + ), + ) + ] + + class SavedFilter(filters.BaseFilterBackend): query_param = "saved" @@ -24,8 +52,8 @@ class SavedFilter(filters.BaseFilterBackend): required=False, location="query", schema=coreschema.String( - title=str(self.query_param), - description=str(_("Wether posts should be saved or not")), + title=force_text(self.query_param), + description=force_text(_("Wether posts should be saved or not")), ), ) ] diff --git a/src/newsreader/news/core/migrations/0001_initial.py b/src/newsreader/news/core/migrations/0001_initial.py index 2fe963f..eb74fc7 100644 --- a/src/newsreader/news/core/migrations/0001_initial.py +++ b/src/newsreader/news/core/migrations/0001_initial.py @@ -8,6 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + initial = True dependencies = [ diff --git a/src/newsreader/news/core/migrations/0002_auto_20190714_1425.py b/src/newsreader/news/core/migrations/0002_auto_20190714_1425.py index 5335614..4d9ad4f 100644 --- a/src/newsreader/news/core/migrations/0002_auto_20190714_1425.py +++ b/src/newsreader/news/core/migrations/0002_auto_20190714_1425.py @@ -7,6 +7,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("core", "0001_initial")] operations = [ diff --git a/src/newsreader/news/core/migrations/0003_post_read.py b/src/newsreader/news/core/migrations/0003_post_read.py index 245c545..8306051 100644 --- a/src/newsreader/news/core/migrations/0003_post_read.py +++ b/src/newsreader/news/core/migrations/0003_post_read.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("core", "0002_auto_20190714_1425")] operations = [ diff --git a/src/newsreader/news/core/migrations/0004_auto_20191116_1315.py b/src/newsreader/news/core/migrations/0004_auto_20191116_1315.py index 19d6bd3..04c6fc7 100644 --- a/src/newsreader/news/core/migrations/0004_auto_20191116_1315.py +++ b/src/newsreader/news/core/migrations/0004_auto_20191116_1315.py @@ -5,6 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ("core", "0003_post_read"), diff --git a/src/newsreader/news/core/migrations/0005_auto_20200412_1955.py b/src/newsreader/news/core/migrations/0005_auto_20200412_1955.py index dbf07fb..0010448 100644 --- a/src/newsreader/news/core/migrations/0005_auto_20200412_1955.py +++ b/src/newsreader/news/core/migrations/0005_auto_20200412_1955.py @@ -7,6 +7,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ("core", "0004_auto_20191116_1315"), diff --git a/src/newsreader/news/core/migrations/0006_auto_20200524_1218.py b/src/newsreader/news/core/migrations/0006_auto_20200524_1218.py index 94c716e..f90b205 100644 --- a/src/newsreader/news/core/migrations/0006_auto_20200524_1218.py +++ b/src/newsreader/news/core/migrations/0006_auto_20200524_1218.py @@ -6,6 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("core", "0005_auto_20200412_1955")] operations = [ diff --git a/src/newsreader/news/core/migrations/0007_auto_20200706_2312.py b/src/newsreader/news/core/migrations/0007_auto_20200706_2312.py index 7721462..751faf9 100644 --- a/src/newsreader/news/core/migrations/0007_auto_20200706_2312.py +++ b/src/newsreader/news/core/migrations/0007_auto_20200706_2312.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("core", "0006_auto_20200524_1218")] operations = [ diff --git a/src/newsreader/news/core/migrations/0008_post_saved.py b/src/newsreader/news/core/migrations/0008_post_saved.py index 40fbd14..08ae2a8 100644 --- a/src/newsreader/news/core/migrations/0008_post_saved.py +++ b/src/newsreader/news/core/migrations/0008_post_saved.py @@ -4,6 +4,7 @@ from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [("core", "0007_auto_20200706_2312")] operations = [ diff --git a/src/newsreader/news/core/serializers.py b/src/newsreader/news/core/serializers.py index c375dde..38619a1 100644 --- a/src/newsreader/news/core/serializers.py +++ b/src/newsreader/news/core/serializers.py @@ -1,6 +1,5 @@ from rest_framework import serializers -from newsreader.news.collection.serializers import RuleSerializer from newsreader.news.core.models import Category, Post @@ -10,8 +9,6 @@ class PostSerializer(serializers.ModelSerializer): ) remoteIdentifier = serializers.CharField(source="remote_identifier", required=False) - rule = RuleSerializer(read_only=True) - class Meta: model = Post fields = ( diff --git a/src/newsreader/news/core/templates/news/core/views/categories.html b/src/newsreader/news/core/templates/news/core/views/categories.html index 78fc663..6a6cdae 100644 --- a/src/newsreader/news/core/templates/news/core/views/categories.html +++ b/src/newsreader/news/core/templates/news/core/views/categories.html @@ -32,7 +32,6 @@ {{ categories_update_url|json_script:"updateUrl" }} {{ categories_create_url|json_script:"createUrl" }} - {{ sidebar_links|json_script:"Links" }} {{ block.super }} {% endblock %} diff --git a/src/newsreader/news/core/templates/news/core/views/category-create.html b/src/newsreader/news/core/templates/news/core/views/category-create.html index 17a42d2..6da166f 100644 --- a/src/newsreader/news/core/templates/news/core/views/category-create.html +++ b/src/newsreader/news/core/templates/news/core/views/category-create.html @@ -1,13 +1,9 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load static %} - {% block content %} - {% url "news:core:categories" as cancel_url %} - -
    -
    - {% include "components/form/form.html" with form=form title="Create category" cancel_url=cancel_url confirm_text="Create category" %} -
    +
    + {% url "news:core:categories" as cancel_url %} + {% include "components/form/form.html" with form=form title="Create category" cancel_url=cancel_url confirm_text="Create category" %}
    {% endblock %} diff --git a/src/newsreader/news/core/templates/news/core/views/category-update.html b/src/newsreader/news/core/templates/news/core/views/category-update.html index 31cd742..1ec1487 100644 --- a/src/newsreader/news/core/templates/news/core/views/category-update.html +++ b/src/newsreader/news/core/templates/news/core/views/category-update.html @@ -1,13 +1,9 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load static %} - {% block content %} - {% url "news:core:categories" as cancel_url %} - -
    -
    - {% include "components/form/form.html" with form=form title="Update category" cancel_url=cancel_url confirm_text="Save category" %} -
    +
    + {% url "news:core:categories" as cancel_url %} + {% include "components/form/form.html" with form=form title="Update category" cancel_url=cancel_url confirm_text="Save category" %}
    {% endblock %} diff --git a/src/newsreader/news/core/templates/news/core/views/homepage.html b/src/newsreader/news/core/templates/news/core/views/homepage.html index 7e59f65..a135314 100644 --- a/src/newsreader/news/core/templates/news/core/views/homepage.html +++ b/src/newsreader/news/core/templates/news/core/views/homepage.html @@ -7,7 +7,6 @@ {% block scripts %} {{ homepageSettings|json_script:"homepageSettings" }} - {{ sidebar_links|json_script:"Links" }} {{ block.super }} {% endblock scripts %} diff --git a/src/newsreader/news/core/tests/endpoints/category/detail/tests.py b/src/newsreader/news/core/tests/endpoints/category/detail/tests.py index f6d35f8..1f42a20 100644 --- a/src/newsreader/news/core/tests/endpoints/category/detail/tests.py +++ b/src/newsreader/news/core/tests/endpoints/category/detail/tests.py @@ -32,7 +32,7 @@ class CategoryDetailViewTestCase(TestCase): data = response.json() self.assertEquals(response.status_code, 404) - self.assertEquals(data["detail"], "No Category matches the given query.") + self.assertEquals(data["detail"], "Not found.") def test_post(self): category = CategoryFactory(user=self.user) @@ -138,10 +138,10 @@ class CategoryReadTestCase(TestCase): def test_category_read(self): category = CategoryFactory(user=self.user) - rules = FeedFactory.create_batch(size=5, category=category) - - for rule in rules: + rules = [ FeedPostFactory.create_batch(size=5, read=False, rule=rule) + for rule in FeedFactory.create_batch(size=5, category=category) + ] response = self.client.post( reverse("api:news:core:categories-read", args=[category.pk]) @@ -164,10 +164,12 @@ class CategoryReadTestCase(TestCase): self.client.logout() category = CategoryFactory(user=self.user) - rules = FeedFactory.create_batch(size=5, category=category, user=self.user) - - for rule in rules: + rules = [ FeedPostFactory.create_batch(size=5, read=False, rule=rule) + for rule in FeedFactory.create_batch( + size=5, category=category, user=self.user + ) + ] response = self.client.post( reverse("api:news:core:categories-read", args=[category.pk]) @@ -178,10 +180,13 @@ class CategoryReadTestCase(TestCase): def test_unauthorized_user(self): other_user = UserFactory() category = CategoryFactory(user=other_user) - rules = FeedFactory.create_batch(size=5, category=category, user=other_user) - for rule in rules: + rules = [ FeedPostFactory.create_batch(size=5, read=False, rule=rule) + for rule in FeedFactory.create_batch( + size=5, category=category, user=other_user + ) + ] response = self.client.post( reverse("api:news:core:categories-read", args=[category.pk]) diff --git a/src/newsreader/news/core/tests/endpoints/category/list/tests.py b/src/newsreader/news/core/tests/endpoints/category/list/tests.py index 610dc58..c822950 100644 --- a/src/newsreader/news/core/tests/endpoints/category/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/category/list/tests.py @@ -1,10 +1,12 @@ import json -from datetime import datetime, timezone +from datetime import datetime from django.test import TestCase from django.urls import reverse +import pytz + from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory @@ -27,15 +29,15 @@ class CategoryListViewTestCase(TestCase): def test_ordering(self): categories = [ CategoryFactory( - created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc), + created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc), user=self.user, ), CategoryFactory( - created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc), + created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc), user=self.user, ), CategoryFactory( - created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc), + created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc), user=self.user, ), ] @@ -117,7 +119,7 @@ class NestedCategoryListViewTestCase(TestCase): def test_simple(self): category = CategoryFactory.create(user=self.user) - FeedFactory.create_batch(size=5, category=category) + rules = FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) @@ -211,7 +213,7 @@ class NestedCategoryListViewTestCase(TestCase): self.client.logout() category = CategoryFactory.create(user=self.user) - FeedFactory.create_batch(size=5, category=category) + rules = FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) @@ -223,7 +225,7 @@ class NestedCategoryListViewTestCase(TestCase): other_user = UserFactory.create() category = CategoryFactory.create(user=other_user) - FeedFactory.create_batch(size=5, category=category) + rules = FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) @@ -282,10 +284,12 @@ class NestedCategoryPostView(TestCase): def test_simple(self): category = CategoryFactory.create(user=self.user) - rules = FeedFactory.create_batch(size=5, category=category, user=self.user) - - for rule in rules: - FeedPostFactory.create_batch(size=5, rule=rule) + rules = { + rule.pk: FeedPostFactory.create_batch(size=5, rule=rule) + for rule in FeedFactory.create_batch( + size=5, category=category, user=self.user + ) + } response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) @@ -316,7 +320,7 @@ class NestedCategoryPostView(TestCase): def test_no_posts(self): category = CategoryFactory.create(user=self.user) - FeedFactory.create_batch(size=5, user=self.user, category=category) + rules = FeedFactory.create_batch(size=5, user=self.user, category=category) response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) @@ -409,7 +413,7 @@ class NestedCategoryPostView(TestCase): reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) ) - self.assertEqual(response.status_code, 404) + self.assertEqual(response.status_code, 403) def test_ordering(self): category = CategoryFactory.create(user=self.user) @@ -426,38 +430,40 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second Reuters post", rule=reuters_rule, - publication_date=datetime(2019, 5, 21, 15, tzinfo=timezone.utc), + publication_date=datetime(2019, 5, 21, 15, tzinfo=pytz.utc), ), FeedPostFactory.create( title="First Reuters post", rule=reuters_rule, - publication_date=datetime(2019, 5, 20, 12, tzinfo=timezone.utc), + publication_date=datetime(2019, 5, 20, 12, tzinfo=pytz.utc), ), ] - FeedPostFactory.create( - title="Second Guardian post", - rule=guardian_rule, - publication_date=datetime(2019, 5, 21, 14, tzinfo=timezone.utc), - ) + guardian_posts = [ + FeedPostFactory.create( + title="Second Guardian post", + rule=guardian_rule, + publication_date=datetime(2019, 5, 21, 14, tzinfo=pytz.utc), + ), + FeedPostFactory.create( + title="First Guardian post", + rule=guardian_rule, + publication_date=datetime(2019, 5, 20, 11, tzinfo=pytz.utc), + ), + ] - FeedPostFactory.create( - title="First Guardian post", - rule=guardian_rule, - publication_date=datetime(2019, 5, 20, 11, tzinfo=timezone.utc), - ) - - FeedPostFactory.create( - title="Second BBC post", - rule=bbc_rule, - publication_date=datetime(2019, 5, 21, 16, tzinfo=timezone.utc), - ) - - FeedPostFactory.create( - title="First BBC post", - rule=bbc_rule, - publication_date=datetime(2019, 5, 20, 13, tzinfo=timezone.utc), - ) + bbc_posts = [ + FeedPostFactory.create( + title="Second BBC post", + rule=bbc_rule, + publication_date=datetime(2019, 5, 21, 16, tzinfo=pytz.utc), + ), + FeedPostFactory.create( + title="First BBC post", + rule=bbc_rule, + publication_date=datetime(2019, 5, 20, 13, tzinfo=pytz.utc), + ), + ] response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) @@ -478,18 +484,22 @@ class NestedCategoryPostView(TestCase): def test_only_posts_from_category_are_returned(self): category = CategoryFactory.create(user=self.user) - CategoryFactory.create(user=self.user) + other_category = CategoryFactory.create(user=self.user) guardian_rule = FeedFactory.create( name="BBC", category=category, user=self.user ) other_rule = FeedFactory.create(name="The Guardian", user=self.user) - FeedPostFactory.create(rule=guardian_rule) - FeedPostFactory.create(rule=guardian_rule) + guardian_posts = [ + FeedPostFactory.create(rule=guardian_rule), + FeedPostFactory.create(rule=guardian_rule), + ] - FeedPostFactory.create(rule=other_rule) - FeedPostFactory.create(rule=other_rule) + other_posts = [ + FeedPostFactory.create(rule=other_rule), + FeedPostFactory.create(rule=other_rule), + ] response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) @@ -500,12 +510,12 @@ class NestedCategoryPostView(TestCase): self.assertEqual(response.status_code, 200) self.assertEqual(len(data["results"]), 2) - self.assertEqual(posts[0]["rule"]["id"], guardian_rule.pk) - self.assertEqual(posts[1]["rule"]["id"], guardian_rule.pk) + self.assertEqual(posts[0]["rule"], guardian_rule.pk) + self.assertEqual(posts[1]["rule"], guardian_rule.pk) - def test_posts(self): + def test_unread_posts(self): category = CategoryFactory.create(user=self.user) - rule = FeedFactory(category=category, user=self.user) + rule = FeedFactory(category=category) FeedPostFactory.create_batch(size=10, rule=rule, read=False) FeedPostFactory.create_batch(size=10, rule=rule, read=True) @@ -514,6 +524,7 @@ class NestedCategoryPostView(TestCase): reverse( "api:news:core:categories-nested-posts", kwargs={"pk": category.pk} ), + {"read": "false"}, ) data = response.json() @@ -524,3 +535,26 @@ class NestedCategoryPostView(TestCase): for post in posts: self.assertEqual(post["read"], False) + + def test_read_posts(self): + category = CategoryFactory.create(user=self.user) + rule = FeedFactory(category=category) + + FeedPostFactory.create_batch(size=20, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) + + response = self.client.get( + reverse( + "api:news:core:categories-nested-posts", kwargs={"pk": category.pk} + ), + {"read": "true"}, + ) + + data = response.json() + posts = data["results"] + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) + + for post in posts: + self.assertEqual(post["read"], True) diff --git a/src/newsreader/news/core/tests/endpoints/post/detail/tests.py b/src/newsreader/news/core/tests/endpoints/post/detail/tests.py index e9a9faa..92444cc 100644 --- a/src/newsreader/news/core/tests/endpoints/post/detail/tests.py +++ b/src/newsreader/news/core/tests/endpoints/post/detail/tests.py @@ -38,7 +38,7 @@ class PostDetailViewTestCase(TestCase): data = response.json() self.assertEqual(response.status_code, 404) - self.assertEqual(data["detail"], "No Post matches the given query.") + self.assertEqual(data["detail"], "Not found.") def test_post(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -111,7 +111,6 @@ class PostDetailViewTestCase(TestCase): data=json.dumps({"title": "This title is very accurate"}), content_type="application/json", ) - data = response.json() self.assertEqual(response.status_code, 200) diff --git a/src/newsreader/news/core/tests/endpoints/post/list/tests.py b/src/newsreader/news/core/tests/endpoints/post/list/tests.py index 467fd05..37f83b0 100644 --- a/src/newsreader/news/core/tests/endpoints/post/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/post/list/tests.py @@ -1,8 +1,10 @@ -from datetime import datetime, timezone +from datetime import datetime from django.test import TestCase from django.urls import reverse +import pytz + from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory @@ -20,8 +22,8 @@ class PostListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() - self.assertEqual(response.status_code, 200) - self.assertEqual(len(data["results"]), 3) + self.assertEquals(response.status_code, 200) + self.assertEquals(len(data["results"]), 3) def test_ordering(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -30,46 +32,48 @@ class PostListViewTestCase(TestCase): FeedPostFactory( title="I'm the first post", rule=rule, - publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=timezone.utc), + publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=pytz.utc), ), FeedPostFactory( title="I'm the second post", rule=rule, - publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc), + publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc), ), FeedPostFactory( title="I'm the third post", rule=rule, - publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=timezone.utc), + publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=pytz.utc), ), ] response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() - self.assertEqual(response.status_code, 200) + self.assertEquals(response.status_code, 200) for index, post in enumerate(posts, start=0): with self.subTest(post=post): self.assertEqual(data["results"][index]["id"], post.pk) - def test_posts(self): + def test_read_posts(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) FeedPostFactory.create_batch(size=20, rule=rule, read=False) FeedPostFactory.create_batch(size=10, rule=rule, read=True) - response = self.client.get(reverse("api:news:core:posts-list")) + response = self.client.get( + reverse("api:news:core:posts-list"), {"read": "true"} + ) data = response.json() posts = data["results"] - self.assertEqual(response.status_code, 200) - self.assertEqual(len(data["results"]), 20) + self.assertEquals(response.status_code, 200) + self.assertEquals(len(data["results"]), 10) for post in posts: with self.subTest(post=post): - self.assertEqual(post["read"], False) + self.assertEqual(post["read"], True) def test_saved_posts(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -84,8 +88,8 @@ class PostListViewTestCase(TestCase): data = response.json() posts = data["results"] - self.assertEqual(response.status_code, 200) - self.assertEqual(len(data["results"]), 10) + self.assertEquals(response.status_code, 200) + self.assertEquals(len(data["results"]), 10) for post in posts: with self.subTest(post=post): diff --git a/src/newsreader/news/core/tests/factories.py b/src/newsreader/news/core/tests/factories.py index 88ce45e..520f940 100644 --- a/src/newsreader/news/core/tests/factories.py +++ b/src/newsreader/news/core/tests/factories.py @@ -1,9 +1,9 @@ -from datetime import timezone - import factory import factory.fuzzy +import pytz from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.reddit import REDDIT_API_URL from newsreader.news.core.models import Category, Post @@ -19,7 +19,7 @@ class PostFactory(factory.django.DjangoModelFactory): title = factory.Faker("sentence") body = factory.Faker("paragraph") author = factory.Faker("name") - publication_date = factory.Faker("date_time_this_year", tzinfo=timezone.utc) + publication_date = factory.Faker("date_time_this_year", tzinfo=pytz.utc) url = factory.Faker("url") remote_identifier = factory.Faker("uuid4") @@ -35,3 +35,10 @@ class PostFactory(factory.django.DjangoModelFactory): class FeedPostFactory(PostFactory): rule = factory.SubFactory("newsreader.news.collection.tests.factories.FeedFactory") + + +class RedditPostFactory(PostFactory): + url = factory.fuzzy.FuzzyText(length=10, prefix=f"{REDDIT_API_URL}/") + rule = factory.SubFactory( + "newsreader.news.collection.tests.factories.SubredditFactory" + ) diff --git a/src/newsreader/news/core/tests/test_views.py b/src/newsreader/news/core/tests/test_views.py index d322dbb..2601b4a 100644 --- a/src/newsreader/news/core/tests/test_views.py +++ b/src/newsreader/news/core/tests/test_views.py @@ -55,7 +55,9 @@ class CategoryCreateViewTestCase(CategoryViewTestCase, TestCase): size=4, user=other_user, category=None ) - CollectionRuleFactory.create_batch(size=3, user=self.user, category=None) + user_rules = CollectionRuleFactory.create_batch( + size=3, user=self.user, category=None + ) data = { "name": "new-category", diff --git a/src/newsreader/news/core/urls.py b/src/newsreader/news/core/urls.py index ea7e957..8096cf8 100644 --- a/src/newsreader/news/core/urls.py +++ b/src/newsreader/news/core/urls.py @@ -14,6 +14,7 @@ from newsreader.news.core.views import ( CategoryCreateView, CategoryListView, CategoryUpdateView, + NewsView, ) diff --git a/src/newsreader/news/core/views.py b/src/newsreader/news/core/views.py index e7fc237..c2ff4d5 100644 --- a/src/newsreader/news/core/views.py +++ b/src/newsreader/news/core/views.py @@ -7,10 +7,9 @@ from django.views.generic.list import ListView from newsreader.news.collection.models import CollectionRule from newsreader.news.core.forms import CategoryForm from newsreader.news.core.models import Category -from newsreader.utils.views import NavListMixin -class NewsView(NavListMixin, TemplateView): +class NewsView(TemplateView): template_name = "news/core/views/homepage.html" def get_context_data(self, **kwargs): @@ -20,6 +19,12 @@ class NewsView(NavListMixin, TemplateView): **context, "homepageSettings": { "feedUrl": reverse_lazy("news:collection:feed-update", args=(0,)), + "subredditUrl": reverse_lazy( + "news:collection:subreddit-update", args=(0,) + ), + "timelineUrl": reverse_lazy( + "news:collection:twitter-timeline-update", args=(0,) + ), "categoriesUrl": reverse_lazy("news:core:category-update", args=(0,)), "timezone": settings.TIME_ZONE, "autoMarking": self.request.user.auto_mark_read, @@ -27,7 +32,7 @@ class NewsView(NavListMixin, TemplateView): } -class CategoryViewMixin(NavListMixin): +class CategoryViewMixin: queryset = Category.objects.prefetch_related("rules").order_by("name") def get_queryset(self): diff --git a/src/newsreader/scss/components/body/_body.scss b/src/newsreader/scss/components/body/_body.scss index 964970a..34bba4c 100644 --- a/src/newsreader/scss/components/body/_body.scss +++ b/src/newsreader/scss/components/body/_body.scss @@ -2,7 +2,7 @@ margin: 0; padding: 0; - font-family: Inter; + font-family: Rubik, sans-serif; font-size: $font-size; } diff --git a/src/newsreader/scss/components/card/_card.scss b/src/newsreader/scss/components/card/_card.scss index 96737a4..fbde877 100644 --- a/src/newsreader/scss/components/card/_card.scss +++ b/src/newsreader/scss/components/card/_card.scss @@ -1,6 +1,3 @@ -@import '../../partials/variables'; -@import '../../lib/mixins'; - .card { display: flex; flex-direction: column; @@ -12,10 +9,6 @@ background-color: var(--background-color); - @media (max-width: $mobile-breakpoint) { - width: initial; - } - &__header { display: flex; justify-content: space-between; @@ -23,17 +16,17 @@ padding: 15px 0; - border-bottom: 2px var(--border-color) solid; + border-bottom: 2px var(--lightest-accent-color) solid; } &__content { display: flex; - padding: 10px 0; + padding: 10px; } &__footer { display: flex; - padding: 10px 0; + padding: 10px; } & .favicon { diff --git a/src/newsreader/scss/components/checkbox-list/_checkbox-list.scss b/src/newsreader/scss/components/checkbox-list/_checkbox-list.scss deleted file mode 100644 index 76b9b97..0000000 --- a/src/newsreader/scss/components/checkbox-list/_checkbox-list.scss +++ /dev/null @@ -1,11 +0,0 @@ -.checkbox-list { - padding: 0; - - &__item { - gap: 10px; - - & > * { - margin: initial; - } - } -} diff --git a/src/newsreader/scss/components/checkbox-list/index.scss b/src/newsreader/scss/components/checkbox-list/index.scss deleted file mode 100644 index 3f7f471..0000000 --- a/src/newsreader/scss/components/checkbox-list/index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './checkbox-list'; diff --git a/src/newsreader/scss/components/fieldset/_fieldset.scss b/src/newsreader/scss/components/fieldset/_fieldset.scss index 8fd35b0..c2588b5 100644 --- a/src/newsreader/scss/components/fieldset/_fieldset.scss +++ b/src/newsreader/scss/components/fieldset/_fieldset.scss @@ -1,8 +1,6 @@ .fieldset { display: flex; flex-direction: column; - flex-wrap: wrap; - gap: 10px 0; padding: 15px; border: none; diff --git a/src/newsreader/scss/components/form/_form.scss b/src/newsreader/scss/components/form/_form.scss index 513fec9..e43110c 100644 --- a/src/newsreader/scss/components/form/_form.scss +++ b/src/newsreader/scss/components/form/_form.scss @@ -1,5 +1,3 @@ -@import '../../partials/variables'; - .form { display: flex; flex-direction: column; @@ -8,10 +6,6 @@ background-color: var(--background-color); - @media (max-width: $mobile-breakpoint) { - width: 100%; - } - &__section { &--last { & .form__fieldset { @@ -50,7 +44,6 @@ display: flex; flex-direction: row; gap: 15px; - flex-wrap: wrap; @include block-padding; } diff --git a/src/newsreader/scss/components/form/_rules-form.scss b/src/newsreader/scss/components/form/_rules-form.scss index f917746..44d4765 100644 --- a/src/newsreader/scss/components/form/_rules-form.scss +++ b/src/newsreader/scss/components/form/_rules-form.scss @@ -1,21 +1,5 @@ -@import '../../partials/variables'; - - .rules-form { @extend .form; width: 90%; - - @media (max-width: $wqhd-breakpoint) { - width: initial; - } - - - & .form__fieldset { - gap: 15px; - - & > * { - margin: initial; - } - } } diff --git a/src/newsreader/scss/components/index.scss b/src/newsreader/scss/components/index.scss index c8a933a..d0419ac 100644 --- a/src/newsreader/scss/components/index.scss +++ b/src/newsreader/scss/components/index.scss @@ -18,13 +18,11 @@ @import './sidebar/index'; @import './table/index'; +@import './integrations/index'; + @import './rules/index'; @import './post/index'; @import './post-message/index'; @import './posts/index'; @import './posts-info/index'; -@import './scroll-to-top/index'; -@import './menu/index'; -@import './nav-list/index'; -@import './checkbox-list/index'; diff --git a/src/newsreader/scss/components/integrations/_integrations.scss b/src/newsreader/scss/components/integrations/_integrations.scss new file mode 100644 index 0000000..815184e --- /dev/null +++ b/src/newsreader/scss/components/integrations/_integrations.scss @@ -0,0 +1,12 @@ +.integrations { + display: flex; + flex-direction: column; + gap: 15px; + + padding: 15px; + + &__controls { + display: flex; + gap: 10px; + } +} diff --git a/src/newsreader/scss/components/integrations/index.scss b/src/newsreader/scss/components/integrations/index.scss new file mode 100644 index 0000000..7f9e759 --- /dev/null +++ b/src/newsreader/scss/components/integrations/index.scss @@ -0,0 +1 @@ +@import './integrations'; diff --git a/src/newsreader/scss/components/list/_list.scss b/src/newsreader/scss/components/list/_list.scss index d90e26e..75e5e94 100644 --- a/src/newsreader/scss/components/list/_list.scss +++ b/src/newsreader/scss/components/list/_list.scss @@ -9,7 +9,9 @@ align-items: center; padding: 10px 0; - gap: 15px; + & > * { + margin: 0 15px; + } } } diff --git a/src/newsreader/scss/components/loading-indicator/_loading-indicator.scss b/src/newsreader/scss/components/loading-indicator/_loading-indicator.scss index d28e87d..0651d1d 100644 --- a/src/newsreader/scss/components/loading-indicator/_loading-indicator.scss +++ b/src/newsreader/scss/components/loading-indicator/_loading-indicator.scss @@ -9,7 +9,7 @@ position: absolute; left: 6px; width: 13px; - background-color: var(--font-color); + background-color: $lavendal-pink; animation: loading-indicator 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite; &:nth-child(1){ diff --git a/src/newsreader/scss/components/main/_main.scss b/src/newsreader/scss/components/main/_main.scss index 2990649..5d0143f 100644 --- a/src/newsreader/scss/components/main/_main.scss +++ b/src/newsreader/scss/components/main/_main.scss @@ -1,22 +1,7 @@ -@import '../../partials/variables'; - .main { - @media (max-width: $mobile-breakpoint) { - display: grid; - grid: [stack] 1fr / min-content [stack] 1fr; + display: flex; + flex-direction: column; + align-items: center; - & .sidebar, .post-message, .posts, #{&}__container { - grid-area: stack; - } - } - - &__container { - display: flex; - flex-direction: column; - align-items: center; - - @media (max-width: $mobile-breakpoint) { - display: initial; - } - } + margin: 20px 0; } diff --git a/src/newsreader/scss/components/menu/_menu.scss b/src/newsreader/scss/components/menu/_menu.scss deleted file mode 100644 index fe0025e..0000000 --- a/src/newsreader/scss/components/menu/_menu.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import '../../partials/variables'; - -.menu { - user-select: none; - touch-action: manipulation; - display: none; - - @media (max-width: $mobile-breakpoint) { - display: initial; - } - - &__icon { - color: var(--font-color); - - padding: 0; - } -} - -#menu-input { - display: none; - - &:checked ~ * .menu__icon::before { - @include font-awesome; - content: "\f410"; - } -} diff --git a/src/newsreader/scss/components/menu/index.scss b/src/newsreader/scss/components/menu/index.scss deleted file mode 100644 index 9cac78c..0000000 --- a/src/newsreader/scss/components/menu/index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './menu'; diff --git a/src/newsreader/scss/components/messages/_messages.scss b/src/newsreader/scss/components/messages/_messages.scss index 0e7a0a0..6e626c9 100644 --- a/src/newsreader/scss/components/messages/_messages.scss +++ b/src/newsreader/scss/components/messages/_messages.scss @@ -1,6 +1,3 @@ -@import '../../partials/variables'; -@import '../../partials/colors'; - .messages { display: flex; flex-direction: column; @@ -9,7 +6,7 @@ width: 100%; margin: 5px 0 20px 0; - color: var(--font-color); + color: $font-color; &__item { width: 80%; @@ -20,10 +17,6 @@ background-color: $transparant-blue; - @media (max-width: $mobile-breakpoint) { - width: 90%; - } - &--error { background-color: $transparant-red; } @@ -49,10 +42,12 @@ } &--fixed &__item { + color: $white; background-color: $blue; } &--fixed &__item--error { + color: $white; background-color: $red; } @@ -61,6 +56,7 @@ } &--fixed &__item--success { + color: $white; background-color: $green; } } diff --git a/src/newsreader/scss/components/modal/_modal.scss b/src/newsreader/scss/components/modal/_modal.scss index a0b764e..4ed5b41 100644 --- a/src/newsreader/scss/components/modal/_modal.scss +++ b/src/newsreader/scss/components/modal/_modal.scss @@ -1,5 +1,3 @@ -@import '../../partials/variables'; - .modal { display: flex; flex-direction: column; @@ -10,7 +8,7 @@ height: 100%; top: 0; - background-color: var(--background-color); + background-color: $dark; &__item { display: flex; @@ -22,17 +20,15 @@ width: 60%; - @media (max-width: $mobile-breakpoint) { - width: initial; - } + background-color: var(--accent-color); } &__header { - padding: 5px 0; + padding: 5px 20px; } &__content { - padding: 10px 0; + padding: 10px 30px; } &__footer { @@ -40,6 +36,6 @@ flex-direction: row; justify-content: space-between; - padding: 10px 0; + padding: 10px; } } diff --git a/src/newsreader/scss/components/modal/_post-modal.scss b/src/newsreader/scss/components/modal/_post-modal.scss index 1a8d18f..f6483fe 100644 --- a/src/newsreader/scss/components/modal/_post-modal.scss +++ b/src/newsreader/scss/components/modal/_post-modal.scss @@ -1,5 +1,3 @@ -@import '../../partials/variables'; - .post-modal { @extend .modal; @@ -7,10 +5,4 @@ padding: 0; cursor: pointer; - - @media (min-width: $tablet-breakpoint) { - background-color: var(--background-color-secondary); - } - - z-index: 1000; } diff --git a/src/newsreader/scss/components/nav-list/_nav-list.scss b/src/newsreader/scss/components/nav-list/_nav-list.scss deleted file mode 100644 index f81ce94..0000000 --- a/src/newsreader/scss/components/nav-list/_nav-list.scss +++ /dev/null @@ -1,16 +0,0 @@ -.nav-list { - display: flex; - justify-content: flex-start; - - list-style-type: none; - - &__item { - margin: 0px 10px; - - & a { - @extend .button; - - color: var(--font-color); - } - } -} diff --git a/src/newsreader/scss/components/nav-list/index.scss b/src/newsreader/scss/components/nav-list/index.scss deleted file mode 100644 index 1201d0b..0000000 --- a/src/newsreader/scss/components/nav-list/index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './nav-list'; diff --git a/src/newsreader/scss/components/navbar/_navbar.scss b/src/newsreader/scss/components/navbar/_navbar.scss index 0fcf3a2..afdacce 100644 --- a/src/newsreader/scss/components/navbar/_navbar.scss +++ b/src/newsreader/scss/components/navbar/_navbar.scss @@ -1,6 +1,3 @@ -@import '../../partials/variables'; -@import '../../lib/functions'; - .nav { display: flex; justify-content: center; @@ -8,43 +5,34 @@ padding: 10px 0; width: 100%; - height: map-deep-get($nav, height); - background-color: var(--background-color); + position: sticky; + top: 0; - border-bottom: 2px var(--border-color) solid; + background-color: var(--lightest-accent-color); - @media (max-width: $mobile-breakpoint) { - justify-content: space-between; - height: map-deep-get($nav, mobile, height); - padding: 10px; - - font-size: map-deep-get($nav, mobile, font-size); - } - - &__list { + ol { display: flex; justify-content: flex-start; width: 80%; list-style-type: none; + } - @media (max-width: $mobile-breakpoint) { - display: none; + &__item { + margin: 0px 10px; + + & a { + @extend .button; + + font-size: 0.9em !important; + font-weight: 600; } } - & .nav-list { - width: 80%; + &__item:last-child { + margin: 0 10px 0 auto; - @media (max-width: $mobile-breakpoint) { - display: none; - } - - &__item:last-child { - margin: 0 10px 0 auto; - - border-right: 2px solid var(--border-color); - } + border-right: 2px solid var(--lighter-accent-color); } } diff --git a/src/newsreader/scss/components/post-message/_post-message.scss b/src/newsreader/scss/components/post-message/_post-message.scss index 712ccd9..e876e7f 100644 --- a/src/newsreader/scss/components/post-message/_post-message.scss +++ b/src/newsreader/scss/components/post-message/_post-message.scss @@ -4,8 +4,8 @@ justify-content: center; align-items: center; - height: max-content; - margin: 20px 0 0 0; + width: 60%; + height: 80vh; &__message { font-size: 16px; diff --git a/src/newsreader/scss/components/post/_post.scss b/src/newsreader/scss/components/post/_post.scss index 200d63f..7ebb7b3 100644 --- a/src/newsreader/scss/components/post/_post.scss +++ b/src/newsreader/scss/components/post/_post.scss @@ -1,44 +1,20 @@ -@import '../../partials/variables'; -@import '../../partials/colors'; -@import '../../lib/functions'; -@import '../../elements/button/'; - .post { display: flex; flex-direction: column; align-items: center; position: relative; - width: 35%; - - @media (max-width: $wqhd-breakpoint) { - width: 50%; - } - - @media (max-width: $mobile-breakpoint) { - width: 100%; - height: 100%; - - margin: 0; - } - - height: max-content; + width: 80%; + height: 90%; margin: 2% auto 5% auto; - padding: 0 0 20px 0; overflow-y: auto; background-color: var(--background-color); - border-radius: 0.25em; cursor: initial; - - &__container { - width: 90%; - } - &__header { display: flex; flex-direction: column; @@ -47,6 +23,8 @@ position: sticky; top: 0; + width: 100%; + background-color: var(--background-color); } @@ -55,15 +33,8 @@ justify-content: flex-end; width: 100%; - padding: 20px 0; + padding: 20px 50px 0; gap: 20px; - - @media (max-width: $mobile-breakpoint) { - justify-content: space-between; - flex-direction: row-reverse; - - gap: 0; - } } &__heading { @@ -71,21 +42,14 @@ flex-direction: column; padding: 20px 0 10px 0; - @media (min-width: $hd-breakpoint) { - width: 80%; - } - - @media (max-width: $hd-breakpoint) { - width: 100%; - padding: 0; - } + width: 75%; } &__title { - font-size: map-deep-get($post, "header-font-size"); + font-size: $header-size; &--read { - text-decoration: line-through; + color: var(--read-color); } } @@ -98,6 +62,8 @@ } &__rule, &__category { + background-color: var(--lightest-accent-color) !important; + & a { color: var(--font-color); } @@ -108,15 +74,7 @@ flex-direction: column; padding: 10px 0 30px 0; - - @media (min-width: $hd-breakpoint) { - width: 72%; - } - - @media (max-width: $hd-breakpoint) { - width: 90%; - padding: 0; - } + width: 75%; & p { padding: 10px 0; @@ -140,62 +98,19 @@ &__close-button { background-color: var(--info-color); - color: $white; + color: var(--font-color); & i { padding: 0 $fa-padding 0 0; } } - &__meta { + &__meta-info { display: flex; flex-direction: row; align-items: center; margin: 15px 0; gap: 10px; - - @media (max-width: $hd-breakpoint) { - flex-wrap: wrap; - } - } - - &__text, &__buttons { - display: flex; - flex-direction: inherit; - align-items: inherit; - gap: inherit; - } - - &__text { - @media (max-width: $hd-breakpoint) { - flex: 100%; - } - } - - &__buttons { - @media (max-width: $hd-breakpoint) { - flex-wrap: inherit; - } - } - - &__link { - @media (max-width: $tablet-breakpoint) { - @include button; - - background-color: var(--info-color); - color: $white; - width: 100px; - } - } - - &__save { - @media (max-width: $tablet-breakpoint) { - @include button; - - background-color: var(--confirm-color); - color: $white; - width: 100px; - } } } diff --git a/src/newsreader/scss/components/posts-info/_posts-info.scss b/src/newsreader/scss/components/posts-info/_posts-info.scss index ca76155..47eae4f 100644 --- a/src/newsreader/scss/components/posts-info/_posts-info.scss +++ b/src/newsreader/scss/components/posts-info/_posts-info.scss @@ -7,27 +7,11 @@ &__date { align-self: center; - - @media (max-width: $mobile-breakpoint){ - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } - - &__link, .saved-icon { - @media (max-width: $mobile-breakpoint){ - display: none; - } } & .badge { & .link { - color: inherit; - } - - @media (max-width: $mobile-breakpoint){ - display: none; + color: var(--font-color); } } } diff --git a/src/newsreader/scss/components/posts/_posts.scss b/src/newsreader/scss/components/posts/_posts.scss index a8a89e6..d40cddb 100644 --- a/src/newsreader/scss/components/posts/_posts.scss +++ b/src/newsreader/scss/components/posts/_posts.scss @@ -1,15 +1,7 @@ -@import '../../partials/variables'; -@import '../../lib/functions'; - .posts { - height: calc(100vh - map-deep-get($nav, height)); - overflow-y: scroll; - padding: 0 0 0 10px; + margin: 0 0 2% 20px; - @media (max-width: $mobile-breakpoint) { - height: calc(100vh - map-deep-get($nav, mobile, height)); - padding: 0; - } + width: 70%; &__list { display: flex; @@ -19,10 +11,6 @@ padding: 0; list-style: none; - - @media (max-width: $mobile-breakpoint) { - width: initial; - } } &__item { @@ -33,22 +21,12 @@ max-width: max-content; - @media (max-width: $mobile-breakpoint) { - max-width: 100vw; - } - &:first-child { padding: 0 10px 10px 10px; } & .badge { - background-color: var(--background-color-secondary); - - @media (max-width: $mobile-breakpoint){ - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } + background-color: var(--lightest-accent-color); } &:last-child { @@ -60,10 +38,10 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - font-size: map-deep-get($post, header-font-size); + font-size: $header-size; &--read { - text-decoration: line-through; + color: var(--read-color); } &:hover { diff --git a/src/newsreader/scss/components/rules/_rules.scss b/src/newsreader/scss/components/rules/_rules.scss index 99ec039..404d0e5 100644 --- a/src/newsreader/scss/components/rules/_rules.scss +++ b/src/newsreader/scss/components/rules/_rules.scss @@ -1,5 +1,3 @@ -@import '../../partials/variables'; - .rules { padding: 0; @@ -8,27 +6,19 @@ justify-content: space-between; align-items: center; - border-bottom-right-radius: .25em; - border-top-right-radius: .25em; - padding: 5px 5px 5px 20px; - @media (max-width: $mobile-breakpoint) { - margin: 10px 0; - padding: 25px 5px 20px 5px; - } - & * { padding: 0 2px 0 2px; } &:hover { cursor: pointer; - background-color: var(--selected-color); + background-color: var(--lighter-accent-color); } &--selected { - background-color: var(--selected-color); + background-color: var(--lighter-accent-color); } } diff --git a/src/newsreader/scss/components/scroll-to-top/_scroll-to-top.scss b/src/newsreader/scss/components/scroll-to-top/_scroll-to-top.scss deleted file mode 100644 index c745c62..0000000 --- a/src/newsreader/scss/components/scroll-to-top/_scroll-to-top.scss +++ /dev/null @@ -1,39 +0,0 @@ -@import '../../partials/variables'; - -.scroll-to-top { - display: flex; - gap: 10px; - - position: fixed; - right: 15%; - bottom: 0; - - margin: 0 0 20px 0; - - @media (max-width: $mobile-breakpoint) { - display: none; - } - - &:hover { - cursor: pointer; - } - - &__icon { - font-style: initial; - padding: 10px; - - background-color: var(--background-color-secondary); - - &--top:before { - @include font-awesome; - - content: "\f062"; - } - - &--bottom:before { - @include font-awesome; - - content: "\f063"; - } - } -} diff --git a/src/newsreader/scss/components/scroll-to-top/index.scss b/src/newsreader/scss/components/scroll-to-top/index.scss deleted file mode 100644 index dcaf7de..0000000 --- a/src/newsreader/scss/components/scroll-to-top/index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './scroll-to-top'; diff --git a/src/newsreader/scss/components/sidebar/_sidebar.scss b/src/newsreader/scss/components/sidebar/_sidebar.scss index 186a382..0521af4 100644 --- a/src/newsreader/scss/components/sidebar/_sidebar.scss +++ b/src/newsreader/scss/components/sidebar/_sidebar.scss @@ -1,77 +1,31 @@ -@import '../../partials/variables'; -@import '../../lib/functions'; - .sidebar { - display: none; // hide the sidebar by default, homepage enables it by default + display: flex; + flex-direction: column; + align-items: center; + align-self: start; - --easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1); - --duration: .6s; + position: sticky; + top: 50px; - height: calc(100vh - map-deep-get($nav, height)); - - font-size: map-deep-get($nav, font-size); - - @media (max-width: $mobile-breakpoint) { - display: grid; - grid-template-columns: [nav] 5fr [escape] 1fr; - height: calc(100vh - map-deep-get($nav, mobile, height)); - - font-size: map-deep-get($sidebar, mobile, font-size); - - overflow: hidden auto; - overscroll-behavior: contain; - - visibility: hidden; - transform: translateX(-110vw); - will-change: transform; - transition: - transform var(--duration) var(--easeOutExpo), - visibility 0s linear var(--duration); - } + width: 20%; &__nav { - display: flex; - flex-direction: column; - overflow-y: scroll; + width: 100%; + max-height: 80vh; + overflow: auto; - background-color: var(--background-color); - - padding: 10px; - } - - &__close { - display: none; - - @media (max-width: $mobile-breakpoint) { - display: initial; - } - } - - &__list { list-style: none; - } - &__item { - @media (max-width: $mobile-breakpoint) { - margin: 10px 0; - } } &__container { display: flex; align-items: center; - border-bottom-right-radius: .25em; - border-top-right-radius: .25em; - padding: 5px; - @media (max-width: $mobile-breakpoint) { - padding: 25px 5px; - } - &--selected, &:hover { - background-color: var(--selected-color); + background-color: var(--lighter-accent-color); } } @@ -97,60 +51,6 @@ } .read-button { - margin: 20px 0 0 10px; - width: max-content; - - @media (max-width: $mobile-breakpoint) { - margin: auto 0 20px 10px; - font-size: inherit; - } - } - - & .nav-list { - display: none; - - &--bordered { - border-bottom: 2px var(--border-color) solid; - } - - @media (max-width: $mobile-breakpoint) { - display: flex; - flex-direction: column; - width: 100%; - - &__item { - margin: 0; - - padding: 25px 15px; - border-bottom-right-radius: 0.25em; - border-bottom-left-radius: 0.25em; - - & a { - - color: inherit; - font-size: inherit; - - align-items: initial; - justify-content: initial; - - padding: 0; - - &:before { - @include font-awesome; - - content: "\f35d"; - padding: 0 20px 0 0; - } - } - } - } - } -} - -@media (max-width: $mobile-breakpoint) { - #menu-input:checked ~ * .sidebar { - visibility: visible; - transform: translateX(0); - transition: transform var(--duration) var(--easeOutExpo); + margin: 20px 0 0 0; } } diff --git a/src/newsreader/scss/components/table/_rules-table.scss b/src/newsreader/scss/components/table/_rules-table.scss index b558045..3be0430 100644 --- a/src/newsreader/scss/components/table/_rules-table.scss +++ b/src/newsreader/scss/components/table/_rules-table.scss @@ -1,17 +1,7 @@ -@import '../../partials/variables'; - .rules-table { - padding: 15px; - - &__heading, &__item { - padding: 10px; - + &__heading { &--select { width: 5%; - - & .checkbox { - margin: 0; - } } &--name { @@ -20,18 +10,10 @@ &--category { width: 15%; - - @media (max-width: $mobile-breakpoint) { - display: none; - } } &--url { width: 40%; - - @media (max-width: $mobile-breakpoint) { - display: none; - } } &--succeeded { diff --git a/src/newsreader/scss/components/table/_table.scss b/src/newsreader/scss/components/table/_table.scss index b8bc660..e39f4c0 100644 --- a/src/newsreader/scss/components/table/_table.scss +++ b/src/newsreader/scss/components/table/_table.scss @@ -2,7 +2,7 @@ table-layout: fixed; background-color: var(--background-color); - width: 100%; + width: 90%; padding: 20px; text-align: left; @@ -10,10 +10,6 @@ &__heading { @extend .h1; - - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; } &__row { @@ -31,7 +27,7 @@ } &__footer { - width: 100%; + width: 80%; padding: 10px 0; } } diff --git a/src/newsreader/scss/elements/badge/_badge.scss b/src/newsreader/scss/elements/badge/_badge.scss index 3842d45..08b4ee8 100644 --- a/src/newsreader/scss/elements/badge/_badge.scss +++ b/src/newsreader/scss/elements/badge/_badge.scss @@ -1,6 +1,3 @@ -@import '../../partials/variables'; -@import '../../partials/colors'; - .badge { display: inline-block; @@ -9,12 +6,7 @@ text-align: center; - background-color: var(--background-color-secondary); + background-color: var(--lighter-accent-color); font-size: small; - border-radius: 0.25em; - - @media (max-width: $mobile-breakpoint) { - font-size: inherit; - } } diff --git a/src/newsreader/scss/elements/button/_button.scss b/src/newsreader/scss/elements/button/_button.scss index 237e37d..b2815aa 100644 --- a/src/newsreader/scss/elements/button/_button.scss +++ b/src/newsreader/scss/elements/button/_button.scss @@ -1,4 +1,4 @@ -@mixin button { +.button { display: flex; align-items: center; @@ -7,20 +7,19 @@ @include button-padding; border: none; - border-radius: 0.25em; + + font-size: 16px; &:hover { cursor: pointer; } - &--success, - &--confirm { + &--success, &--confirm { + color: var(--confirm-button-font-color) !important; background-color: var(--confirm-color); - color: $white !important; } - &--error, - &--cancel { + &--error, &--cancel { color: $white !important; background-color: var(--danger-color); } @@ -30,17 +29,26 @@ background-color: var(--info-color); } - &--disabled { - color: var(--font-color) !important; - background-color: var(--background-color-secondary) !important; + &--reddit { + color: $white !important; + background-color: $reddit-orange; &:hover { - cursor: default; + background-color: lighten($reddit-orange, 5%); } } -} + &--twitter { + color: $white !important; + background-color: $twitter-blue; -.button { - @include button; + &:hover { + background-color: lighten($twitter-blue, 5%); + } + } + + &--disabled { + color: var(--font-color) !important; + background-color: $gray !important; + } } diff --git a/src/newsreader/scss/elements/button/_read-button.scss b/src/newsreader/scss/elements/button/_read-button.scss index bd09cbf..41bd3cc 100644 --- a/src/newsreader/scss/elements/button/_read-button.scss +++ b/src/newsreader/scss/elements/button/_read-button.scss @@ -1,18 +1,10 @@ -@import '../../partials/variables'; -@import '../../partials/colors'; - .read-button { @extend .button; - color: var(--confirm-font-color); - + color: var(--confirm-button-font-color); background-color: var(--confirm-color); & i { padding: 0 $fa-padding 0 0; } - - @media (max-width: $mobile-breakpoint) { - width: max-content; - } } diff --git a/src/newsreader/scss/elements/checkbox/_checkbox.scss b/src/newsreader/scss/elements/checkbox/_checkbox.scss index b818723..174f348 100644 --- a/src/newsreader/scss/elements/checkbox/_checkbox.scss +++ b/src/newsreader/scss/elements/checkbox/_checkbox.scss @@ -2,6 +2,8 @@ display: block; height: 20px; width: 20px; + margin: 0 0 0 20px; + & input[type=checkbox] { position: absolute; @@ -12,7 +14,7 @@ &:checked + .checkbox__label { .checkbox__box { - background-color: var(--info-color); + background-color: var(--lightest-accent-color); } } } @@ -27,7 +29,7 @@ height: 100%; width: 100%; - border: 1.5px solid var(--border-color); + border: 2px solid var(--lighter-accent-color); cursor: pointer; } } diff --git a/src/newsreader/scss/elements/help-text/_help-text.scss b/src/newsreader/scss/elements/help-text/_help-text.scss index d91adc0..a90552d 100644 --- a/src/newsreader/scss/elements/help-text/_help-text.scss +++ b/src/newsreader/scss/elements/help-text/_help-text.scss @@ -1,8 +1,7 @@ .help-text { @extend .small; - padding: 10px 0; - + padding: 5px 15px; } .helptext { diff --git a/src/newsreader/scss/elements/input/_input.scss b/src/newsreader/scss/elements/input/_input.scss index fd7a231..84c2470 100644 --- a/src/newsreader/scss/elements/input/_input.scss +++ b/src/newsreader/scss/elements/input/_input.scss @@ -2,8 +2,12 @@ @include text-padding; color: var(--font-color); - background-color: var(--background-color-secondary); - border: 1px var(--border-color) solid; + background-color: var(--accent-color); + border: 1px var(--lighter-accent-color) solid; + + &:focus { + border: 1px var(--lightest-accent-color) solid; + } &[type="file"] { width: 40%; @@ -11,6 +15,7 @@ &[type="checkbox"] { align-self: flex-start; + margin: 0 0 0 10px; } } diff --git a/src/newsreader/scss/elements/label/_label.scss b/src/newsreader/scss/elements/label/_label.scss index abf59a2..6481b02 100644 --- a/src/newsreader/scss/elements/label/_label.scss +++ b/src/newsreader/scss/elements/label/_label.scss @@ -1,5 +1,5 @@ .label { - padding: 10px 0; + @include text-padding; } label { diff --git a/src/newsreader/scss/lib/_font-awesome.scss b/src/newsreader/scss/lib/_font-awesome.scss index b422bd6..a06b282 100644 --- a/src/newsreader/scss/lib/_font-awesome.scss +++ b/src/newsreader/scss/lib/_font-awesome.scss @@ -1,5 +1,5 @@ -$fa-font-path: '~@fortawesome/fontawesome-free/webfonts'; +$fa-font-path: '/static/fonts'; -@import '~@fortawesome/fontawesome-free/scss/fontawesome'; -@import '~@fortawesome/fontawesome-free/scss/solid'; -@import '~@fortawesome/fontawesome-free/scss/regular'; +@import '@fortawesome/fontawesome-free/scss/fontawesome'; +@import '@fortawesome/fontawesome-free/scss/solid'; +@import '@fortawesome/fontawesome-free/scss/regular'; diff --git a/src/newsreader/scss/lib/_functions.scss b/src/newsreader/scss/lib/_functions.scss deleted file mode 100644 index cb8189f..0000000 --- a/src/newsreader/scss/lib/_functions.scss +++ /dev/null @@ -1,7 +0,0 @@ -@function map-deep-get($map, $keys...) { - @each $key in $keys { - $map: map-get($map, $key); - } - - @return $map; -} diff --git a/src/newsreader/scss/pages/homepage/index.scss b/src/newsreader/scss/pages/homepage/index.scss index 99260a7..30f5a50 100644 --- a/src/newsreader/scss/pages/homepage/index.scss +++ b/src/newsreader/scss/pages/homepage/index.scss @@ -1,26 +1,9 @@ #homepage--page { - background-color: initial; + display: flex; + flex-direction: row; + align-items: initial; + width: 100%; - display: grid; - - @media (min-width: $mobile-breakpoint) { - grid: [stack] 1fr/20% [stack] auto; // TODO: remove this line? - } - - @media (min-width: $hd-breakpoint) { - grid: [stack] 1fr/15% [stack] auto; - } - - @media (min-width: $wqhd-breakpoint) { - grid: [stack] 1fr/12% [stack] auto; - } - - @media (min-width: $uhd-breakpoint) { - grid: [stack] 1fr/10% [stack] auto; - } - - & .sidebar { - display: grid; - grid-template-columns: [nav] 100% [escape] 0; - } + margin: 20px 0 0 0; + background-color: initial; } diff --git a/src/newsreader/scss/pages/index.scss b/src/newsreader/scss/pages/index.scss index 44ca8a7..2ac0bb2 100644 --- a/src/newsreader/scss/pages/index.scss +++ b/src/newsreader/scss/pages/index.scss @@ -12,3 +12,4 @@ @import './rules/index'; @import './settings/index'; +@import './integrations/index'; diff --git a/src/newsreader/scss/pages/integrations/index.scss b/src/newsreader/scss/pages/integrations/index.scss new file mode 100644 index 0000000..ccf52c3 --- /dev/null +++ b/src/newsreader/scss/pages/integrations/index.scss @@ -0,0 +1,5 @@ +#integrations--page { + .section { + width: 70%; + } +} diff --git a/src/newsreader/scss/pages/login/index.scss b/src/newsreader/scss/pages/login/index.scss index 68ac32d..f1805ed 100644 --- a/src/newsreader/scss/pages/login/index.scss +++ b/src/newsreader/scss/pages/login/index.scss @@ -1,24 +1,11 @@ #login--page { + margin: 5% auto; + width: 50%; + & .form { @extend .form; - width: 20%; - - @media (max-width: $wqhd-breakpoint) { - width: 30%; - } - - @media (max-width: $hd-breakpoint) { - width: 40%; - } - - @media (max-width: $tablet-breakpoint) { - width: 50%; - } - - @media (max-width: $mobile-breakpoint) { - width: 100%; - } + width: 100%; h4 { margin: 0; @@ -34,6 +21,9 @@ &__fieldset { @extend .form__fieldset; + + &--last { + } } } } diff --git a/src/newsreader/scss/pages/rules/index.scss b/src/newsreader/scss/pages/rules/index.scss index de69b2d..64f46b4 100644 --- a/src/newsreader/scss/pages/rules/index.scss +++ b/src/newsreader/scss/pages/rules/index.scss @@ -1,3 +1,5 @@ #rules--page { - // TODO: remove scss + & .table { + width: 100%; + } } diff --git a/src/newsreader/scss/pages/settings/index.scss b/src/newsreader/scss/pages/settings/index.scss index 8f1e57a..c52f46b 100644 --- a/src/newsreader/scss/pages/settings/index.scss +++ b/src/newsreader/scss/pages/settings/index.scss @@ -3,7 +3,6 @@ &__section { &--last { & .fieldset { - flex-wrap: wrap; gap: 15px; justify-content: flex-start; } diff --git a/src/newsreader/scss/partials/_colors.scss b/src/newsreader/scss/partials/_colors.scss index d2433f6..621fcfe 100644 --- a/src/newsreader/scss/partials/_colors.scss +++ b/src/newsreader/scss/partials/_colors.scss @@ -1,61 +1,59 @@ -$orange: #ff2a51; -$green: #007936; -$red: #d30038; -$blue: #0085f2; +$orange: rgba(255, 212, 153, 1); +$green: rgba(89, 181, 128, 1); +$red: lighten(rgba(231, 76, 60, 1), 10%); +$gray: rgba(227, 227, 227, 1); +$blue: rgba(111, 164, 196, 1); -$white: #fff; -$black: #000; +$white: rgba(255, 255, 255, 1); +$black: rgba(0, 0, 0, 1); +$dark: rgba(0, 0, 0, 0.4); + +$reddit-orange: rgba(255, 69, 0, 1); +$twitter-blue: rgba(29, 155, 240, 1); $transparant-red: transparentize($red, 0.8); $transparant-blue: transparentize($blue, 0.8); $transparant-orange: transparentize($orange, 0.4); $transparant-green: transparentize($green, 0.4); +$azureish-white: rgba(205, 230, 245, 1); +$gainsboro: rgba(238, 238, 238, 1); +$nickel: rgba(112, 112, 120, 1); +$lavendal-pink: rgba(162, 155, 254, 1); + +$focus-blue: darken($azureish-white, +10%); +$checkbox-blue: rgba(34, 170, 253, 1); + // White theme -$background-color: $white; -$background-color-secondary: #f9f9fb; +$background-color: rgba(255, 249, 176, 1); -$font-color: #1b1b1b; +$font-color: rgba(83, 87, 91, 1); +$link-color: rgba(45, 142, 202, 1); +$read-color: darken($gainsboro, 10%); +$confirm-button-font-color: rgba(255, 255, 255, 1); -$link-color: #0069c2; -$selected-color: #0085f230; -$read-color: darken($font-color, 10%); +$accent-color: rgba(255, 171, 115, 1); +$lighter-accent-color: rgba(255, 211, 132, 1); +$lightest-accent-color: rgba(255, 174, 192, 1); -$confirm-color: $green; -$confirm-font-color: $white; - -$danger-color: $red; -$danger-font-color: $white; - -$warning-color: $orange; -$warning-font-color: $white; - -$info-color: $blue; -$info-font-color: $white; - -$sidebar-background-color: $background-color-secondary; - -$border-color: #cdcdcd; +$confirm-color: rgba(117, 207, 184, 1); +$danger-color: rgba(237, 118, 105, 1); +$warning-color: rgba(255, 218, 119, 1); +$info-color: rgba(162, 213, 242, 1); // Dark theme -$dark-background-color: #1b1b1b; -$dark-background-color-secondary: #313131; - -$dark-font-color: #cccccc; +$dark-background-color: rgba(29, 45, 80, 1); +$dark-font-color: darken($gray, 10%); $dark-link-color: $link-color; -$dark-read-color: darken($dark-font-color, 5%); +$dark-read-color: darken($dark-font-color, 20%); +$dark-confirm-button-font-color: $dark-font-color; -$dark-confirm-color: $green; -$dark-confirm-font-color: $white; +$dark-accent-color: rgba(19, 59, 92, 1); +$dark-lighter-accent-color: rgba(30, 95, 116, 1); +$dark-lightest-accent-color: rgba(88, 61, 114, 1); -$dark-danger-color: $red; -$dark-danger-font-color: $white; - -$dark-warning-color: $orange; -$dark-warning-font-color: $white; - -$dark-info-color: $blue; -$dark-info-font-color: $white; - -$dark-sidebar-background-color: $dark-background-color-secondary; +$dark-confirm-color: rgba(0, 121, 101, 1); +$dark-danger-color: rgba(175, 45, 45, 1); +$dark-warning-color: rgba(238, 187, 77, 1); +$dark-info-color: rgba(31, 111, 139, 1); diff --git a/src/newsreader/scss/partials/_fonts.scss b/src/newsreader/scss/partials/_fonts.scss index 5b07f26..934db2e 100644 --- a/src/newsreader/scss/partials/_fonts.scss +++ b/src/newsreader/scss/partials/_fonts.scss @@ -1,15 +1,12 @@ @font-face { - font-family: Inter; - font-style: normal; - font-display: swap; - src: url('../assets/fonts/Inter-VariableFont_opsz,wght.ttf'); + font-family: Rubik; + src: url('../assets/fonts/Rubik-Regular.ttf'); } @font-face { - font-family: Inter; - font-style: italic; - font-display: swap; - src: url('../assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf'); + font-family: Rubik; + src: url('../assets/fonts/Rubik-Bold.ttf'); + font-weight: bold; } @font-face { diff --git a/src/newsreader/scss/partials/_root.scss b/src/newsreader/scss/partials/_root.scss index afc9617..cc8cebf 100644 --- a/src/newsreader/scss/partials/_root.scss +++ b/src/newsreader/scss/partials/_root.scss @@ -1,35 +1,33 @@ :root { --background-color: #{$background-color}; - --background-color-secondary: #{$background-color-secondary}; --font-color: #{$font-color}; --link-color: #{$link-color}; - --selected-color: #{$selected-color}; + --read-color: #{$read-color}; + --confirm-button-font-color: #{$confirm-button-font-color}; + + --accent-color: #{$accent-color}; + --lighter-accent-color: #{$lighter-accent-color}; + --lightest-accent-color: #{$lightest-accent-color}; --confirm-color: #{$confirm-color}; - --confirm-font-color: #{$confirm-font-color}; - --danger-color: #{$danger-color}; - --danger-font-color: #{$danger-color}; - --warning-color: #{$warning-color}; - --warning-font-color: #{$warning-color}; - --info-color: #{$info-color}; - --info-font-color: #{$info-color}; - - --border-color: #{$border-color}; &.dark-theme { --background-color: #{$dark-background-color}; - --background-color-secondary: #{$dark-background-color-secondary}; --font-color: #{$dark-font-color}; --link-color: #{$dark-link-color}; + --read-color: #{$dark-read-color}; + --confirm-button-font-color: #{$dark-confirm-button-font-color}; + + --accent-color: #{$dark-accent-color}; + --lighter-accent-color: #{$dark-lighter-accent-color}; + --lightest-accent-color: #{$dark-lightest-accent-color}; --confirm-color: #{$dark-confirm-color}; - --confirm-font-color: #{$dark-confirm-font-color}; - --danger-color: #{$dark-danger-color}; --warning-color: #{$dark-warning-color}; --info-color: #{$dark-info-color}; diff --git a/src/newsreader/scss/partials/_variables.scss b/src/newsreader/scss/partials/_variables.scss index b4165d3..d8e8261 100644 --- a/src/newsreader/scss/partials/_variables.scss +++ b/src/newsreader/scss/partials/_variables.scss @@ -1,36 +1,4 @@ $fa-padding: 7px; -// Fonts +$header-size: 1.2em; $font-size: 1.1em; -$font-size-small: 0.833em; - -// Dimensions -$mobile-breakpoint: 540px; -$tablet-breakpoint: 1280px; -$hd-breakpoint: 1920px; -$wqhd-breakpoint: 2560px; -$uhd-breakpoint: 3840px; - -$nav: ( - height: 50px, - font-size: $font-size-small, - - mobile: ( - height: 75px, - font-size: 1.5em - ) -); - -// Post -$post: ( - header-font-size: 1.2em -); - -// Sidebar -$sidebar: ( - font-size: $font-size, - - mobile: ( - font-size: 1.2em, - ) -); diff --git a/src/newsreader/templates/base.html b/src/newsreader/templates/base.html index 9900b37..3c65329 100644 --- a/src/newsreader/templates/base.html +++ b/src/newsreader/templates/base.html @@ -4,7 +4,6 @@ Newreader - {% block head %} @@ -12,14 +11,22 @@ - - diff --git a/src/newsreader/templates/components/nav-list/nav-list.html b/src/newsreader/templates/components/nav-list/nav-list.html deleted file mode 100644 index 1c76122..0000000 --- a/src/newsreader/templates/components/nav-list/nav-list.html +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/src/newsreader/templates/password-reset/password-reset-complete.html b/src/newsreader/templates/password-reset/password-reset-complete.html index ddd41e7..0b7796f 100755 --- a/src/newsreader/templates/password-reset/password-reset-complete.html +++ b/src/newsreader/templates/password-reset/password-reset-complete.html @@ -1,15 +1,13 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load i18n %} {% block content %} +
    {% trans "Password reset complete" as header_text %} {% blocktrans asvar content %} You may now log in {% endblocktrans %} -
    -
    - {% include "components/card/card.html" with header_text=header_text content=content %} -
    -
    + {% include "components/card/card.html" with header_text=header_text content=content %} +
    {% endblock %} diff --git a/src/newsreader/templates/password-reset/password-reset-confirm.html b/src/newsreader/templates/password-reset/password-reset-confirm.html index 852283c..d0d5037 100755 --- a/src/newsreader/templates/password-reset/password-reset-confirm.html +++ b/src/newsreader/templates/password-reset/password-reset-confirm.html @@ -1,4 +1,4 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load i18n %} {% block meta %} @@ -8,25 +8,23 @@ {% endblock %} {% block content %} -
    -
    - {% if validlink %} - {% url 'accounts:login' as cancel_url %} - {% trans "Enter your new password below to reset your password:" as title %} - {% trans "Change password" as confirm_text %} - {% include "components/form/form.html" with form=form title=title confirm_text=confirm_text cancel_url=cancel_url %} - {% else %} - {% trans "Password reset unsuccessful" as header_text %} - {% url 'accounts:password-reset' as reset_url %} - {% blocktrans asvar content %} - Password reset unsuccessful. Please - try again. - {% endblocktrans %} +
    + {% if validlink %} + {% url 'accounts:login' as cancel_url %} + {% trans "Enter your new password below to reset your password:" as title %} + {% trans "Change password" as confirm_text %} + {% include "components/form/form.html" with form=form title=title confirm_text=confirm_text cancel_url=cancel_url %} + {% else %} + {% trans "Password reset unsuccessful" as header_text %} + {% url 'accounts:password-reset' as reset_url %} + {% blocktrans asvar content %} + Password reset unsuccessful. Please + try again. + {% endblocktrans %} - {% include "components/card/card.html" with header_text=header_text content=content %} - {% endif %} -
    -
    + {% include "components/card/card.html" with header_text=header_text content=content %} + {% endif %} +
    {% endblock %} {# This is used by django.contrib.auth #} diff --git a/src/newsreader/templates/password-reset/password-reset-done.html b/src/newsreader/templates/password-reset/password-reset-done.html index b160339..7012439 100755 --- a/src/newsreader/templates/password-reset/password-reset-done.html +++ b/src/newsreader/templates/password-reset/password-reset-done.html @@ -1,18 +1,16 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% load static i18n %} {% block title %}{% trans "Password reset" %}{% endblock %} {% block content %} +
    {% trans "Password reset" as header_text %} {% blocktrans asvar content %} We have sent you an email with a link to reset your password. Please check your email and click the link to continue. {% endblocktrans %} -
    -
    - {% include "components/card/card.html" with header_text=header_text content=content %} -
    -
    + {% include "components/card/card.html" with header_text=header_text content=content %} +
    {% endblock %} diff --git a/src/newsreader/templates/password-reset/password-reset.html b/src/newsreader/templates/password-reset/password-reset.html index 0454f4e..97e5678 100644 --- a/src/newsreader/templates/password-reset/password-reset.html +++ b/src/newsreader/templates/password-reset/password-reset.html @@ -1,9 +1,7 @@ -{% extends "sidebar.html" %} +{% extends "base.html" %} {% block content %} -
    -
    - {% include "password-reset/password-reset-form.html" with form=form title="Reset password" confirm_text="Reset password" %} -
    -
    +
    + {% include "password-reset/password-reset-form.html" with form=form title="Reset password" confirm_text="Reset password" %} +
    {% endblock %} diff --git a/src/newsreader/templates/registration/activation_complete.html b/src/newsreader/templates/registration/activation_complete.html new file mode 100755 index 0000000..f8dd91b --- /dev/null +++ b/src/newsreader/templates/registration/activation_complete.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{% load i18n %} + +{% comment %} +**registration/activation_complete.html** + +Used after successful account activation. This template has no context +variables of its own, and should simply inform the user that their +account is now active. +{% endcomment %} + +{% block content %} +
    + {% trans "Account activated" as header_text %} + + {% if user.is_authenticated %} + {% trans "Your account is activated. You can now log in." as content %} + {% else %} + {% trans "Your account is activated." as content %} + {% endif %} + + {% include "components/card/card.html" with header_text=header_text content=content %} +
    +{% endblock %} diff --git a/src/newsreader/templates/registration/activation_email.html b/src/newsreader/templates/registration/activation_email.html new file mode 100644 index 0000000..8773b29 --- /dev/null +++ b/src/newsreader/templates/registration/activation_email.html @@ -0,0 +1,72 @@ +{% load i18n %} + + + + + {{ site.name }} {% trans "registration" %} + + + +

    + {% blocktrans with site_name=site.name %} + You (or someone pretending to be you) have asked to register an account at + {{ site_name }}. If this wasn't you, please ignore this email + and your address will be removed from our records. + {% endblocktrans %} +

    +

    + {% blocktrans %} + To activate this account, please click the following link within the next + {{ expiration_days }} days: + {% endblocktrans %} +

    + +

    + + {{site.domain}}{% url 'accounts:activate' activation_key %} + +

    +

    + {% blocktrans with site_name=site.name %} + Sincerely, + {{ site_name }} Management + {% endblocktrans %} +

    + + + + + +{% comment %} +**registration/activation_email.html** + +Used to generate the html body of the activation email. Should display a +link the user can click to activate the account. This template has the +following context: + +``activation_key`` + The activation key for the new account. + +``expiration_days`` + The number of days remaining during which the account may be + activated. + +``site`` + An object representing the site on which the user registered; + depending on whether ``django.contrib.sites`` is installed, this + may be an instance of either ``django.contrib.sites.models.Site`` + (if the sites application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if not). Consult `the + documentation for the Django sites framework + `_ for + details regarding these objects' interfaces. + +``user`` + The new user account + +``request`` + ``HttpRequest`` instance for better flexibility. + For example it can be used to compute absolute register URL: + + {{ request.scheme }}://{{ request.get_host }}{% url 'accounts:activate' activation_key %} +{% endcomment %} diff --git a/src/newsreader/templates/registration/activation_email.txt b/src/newsreader/templates/registration/activation_email.txt new file mode 100644 index 0000000..d07e785 --- /dev/null +++ b/src/newsreader/templates/registration/activation_email.txt @@ -0,0 +1,52 @@ +{% load i18n %} +{% blocktrans with site_name=site.name %} +You (or someone pretending to be you) have asked to register an account at +{{ site_name }}. If this wasn't you, please ignore this email +and your address will be removed from our records. +{% endblocktrans %} +{% blocktrans %} +To activate this account, please click the following link within the next +{{ expiration_days }} days: +{% endblocktrans %} + +http://{{site.domain}}{% url 'accounts:activate' activation_key %} + +{% blocktrans with site_name=site.name %} +Sincerely, +{{ site_name }} Management +{% endblocktrans %} + + +{% comment %} +**registration/activation_email.txt** + +Used to generate the text body of the activation email. Should display a +link the user can click to activate the account. This template has the +following context: + +``activation_key`` + The activation key for the new account. + +``expiration_days`` + The number of days remaining during which the account may be + activated. + +``site`` + An object representing the site on which the user registered; + depending on whether ``django.contrib.sites`` is installed, this + may be an instance of either ``django.contrib.sites.models.Site`` + (if the sites application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if not). Consult `the + documentation for the Django sites framework + `_ for + details regarding these objects' interfaces. + +``user`` + The new user account + +``request`` + ``HttpRequest`` instance for better flexibility. + For example it can be used to compute absolute register URL: + + {{ request.scheme }}://{{ request.get_host }}{% url 'accounts:activate' activation_key %} +{% endcomment %} diff --git a/src/newsreader/templates/registration/activation_email_subject.txt b/src/newsreader/templates/registration/activation_email_subject.txt new file mode 100644 index 0000000..da0ddeb --- /dev/null +++ b/src/newsreader/templates/registration/activation_email_subject.txt @@ -0,0 +1,28 @@ +{% load i18n %}{% trans "Account activation on" %} {{ site.name }} + + +{% comment %} +**registration/activation_email_subject.txt** + +Used to generate the subject line of the activation email. Because the +subject line of an email must be a single line of text, any output +from this template will be forcibly condensed to a single line before +being used. This template has the following context: + +``activation_key`` + The activation key for the new account. + +``expiration_days`` + The number of days remaining during which the account may be + activated. + +``site`` + An object representing the site on which the user registered; + depending on whether ``django.contrib.sites`` is installed, this + may be an instance of either ``django.contrib.sites.models.Site`` + (if the sites application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if not). Consult `the + documentation for the Django sites framework + `_ for + details regarding these objects' interfaces. +{% endcomment %} diff --git a/src/newsreader/templates/registration/activation_failure.html b/src/newsreader/templates/registration/activation_failure.html new file mode 100644 index 0000000..c99cc34 --- /dev/null +++ b/src/newsreader/templates/registration/activation_failure.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% load i18n %} + +{% comment %} +**registration/activate.html** + +Used if account activation fails. With the default setup, has the following context: + +``activation_key`` + The activation key used during the activation attempt. +{% endcomment %} + +{% block content %} +
    + {% trans "Activation Failure" as header_text %} + {% trans "Account activation failed." as content %} + {% include "components/card/card.html" with header_text=header_text content=content %} +
    +{% endblock %} diff --git a/src/newsreader/templates/registration/activation_resend_complete.html b/src/newsreader/templates/registration/activation_resend_complete.html new file mode 100644 index 0000000..6d01fee --- /dev/null +++ b/src/newsreader/templates/registration/activation_resend_complete.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} +{% load static i18n %} + +{% block title %}{% trans "Account Activation Resent" %}{% endblock %} + +{% comment %} +**registration/resend_activation_complete.html** +Used after form for resending account activation is submitted. By default has +the following context: + +``email`` + The email address submitted in the resend activation form. +{% endcomment %} + +{% block content %} +
    + {% trans "Account activation resent" as header_text %} + {% blocktrans asvar content %} + We have sent an email to {{ email }} with further instructions. + {% endblocktrans %} + {% include "components/card/card.html" with header_text=header_text content=content %} +
    +{% endblock %} diff --git a/src/newsreader/templates/registration/activation_resend_form.html b/src/newsreader/templates/registration/activation_resend_form.html new file mode 100644 index 0000000..5f0dd82 --- /dev/null +++ b/src/newsreader/templates/registration/activation_resend_form.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
    + {% url "accounts:login" as cancel_url %} + {% include "components/form/form.html" with form=form title="Resend activation code" cancel_url=cancel_url confirm_text="Resend code" %} +
    +{% endblock %} diff --git a/src/newsreader/templates/registration/registration_closed.html b/src/newsreader/templates/registration/registration_closed.html new file mode 100755 index 0000000..c7cfd9a --- /dev/null +++ b/src/newsreader/templates/registration/registration_closed.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% load static i18n %} + +{% block content %} +
    + {% trans "Registration is closed" as header_text %} + {% trans "Sorry, but registration is closed at this moment. Come back later." as content %} + {% include "components/card/card.html" with header_text=header_text content=content %} +
    +{% endblock %} diff --git a/src/newsreader/templates/registration/registration_complete.html b/src/newsreader/templates/registration/registration_complete.html new file mode 100755 index 0000000..ccf70b2 --- /dev/null +++ b/src/newsreader/templates/registration/registration_complete.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% load i18n %} + +{% comment %} +**registration/registration_complete.html** + +Used after successful completion of the registration form. This +template has no context variables of its own, and should simply inform +the user that an email containing account-activation information has +been sent. +{% endcomment %} + +{% block content %} +
    + {% trans "Activation email sent" as header_text %} + {% trans "Please check your email to complete the registration process." as content %} + {% include "components/card/card.html" with header_text=header_text content=content %} +
    +{% endblock %} diff --git a/src/newsreader/templates/registration/registration_form.html b/src/newsreader/templates/registration/registration_form.html new file mode 100644 index 0000000..ccc07c9 --- /dev/null +++ b/src/newsreader/templates/registration/registration_form.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
    + {% url "accounts:login" as cancel_url %} + {% include "components/form/form.html" with form=form title="Register" cancel_url=cancel_url confirm_text="Register" %} +
    +{% endblock %} diff --git a/src/newsreader/templates/sidebar.html b/src/newsreader/templates/sidebar.html deleted file mode 100644 index bf99634..0000000 --- a/src/newsreader/templates/sidebar.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "base.html" %} - -{% block scripts %} - {{ sidebar_links|json_script:"Links" }} - - {{ block.super }} -{% endblock %} diff --git a/src/newsreader/urls.py b/src/newsreader/urls.py index 2e4c9c3..0779b29 100644 --- a/src/newsreader/urls.py +++ b/src/newsreader/urls.py @@ -3,6 +3,9 @@ from django.contrib import admin from django.contrib.auth.decorators import login_required from django.urls import include, path +from drf_yasg import openapi +from drf_yasg.views import get_schema_view + from newsreader.accounts.urls import urlpatterns as login_urls from newsreader.news.core.views import NewsView from newsreader.news.urls import endpoints as news_endpoints @@ -11,12 +14,16 @@ from newsreader.news.urls import urlpatterns as news_patterns api_patterns = [path("api/", include((news_endpoints, "news")))] +schema_info = openapi.Info(title="Newsreader API", default_version="v1") +schema_view = get_schema_view(schema_info, patterns=api_patterns) + urlpatterns = [ path("", login_required(NewsView.as_view()), name="index"), path("", include((news_patterns, "news"))), path("", include((api_patterns, "api"))), path("accounts/", include((login_urls, "accounts")), name="accounts"), path("admin/", admin.site.urls, name="admin"), + path("api/", schema_view.with_ui("swagger"), name="api"), path("api/auth/", include("rest_framework.urls"), name="rest_framework"), ] diff --git a/src/newsreader/utils/celery.py b/src/newsreader/utils/celery.py index 4a03e71..84572c6 100644 --- a/src/newsreader/utils/celery.py +++ b/src/newsreader/utils/celery.py @@ -1,7 +1,7 @@ -from time import monotonic - from django.core.cache import cache +from celery.five import monotonic + LOCK_EXPIRE = 60 * 10 # 10 minutes diff --git a/src/newsreader/utils/opml.py b/src/newsreader/utils/opml.py index 7657a03..1aca0fd 100644 --- a/src/newsreader/utils/opml.py +++ b/src/newsreader/utils/opml.py @@ -21,7 +21,7 @@ def parse_opml(file, user, skip_existing=False): validate = URLValidator(schemes=["http", "https"]) for element in root.iter(tag="outline"): - if "xmlUrl" not in element.keys(): + if not "xmlUrl" in element.keys(): continue feed_url = element.get("xmlUrl") diff --git a/src/newsreader/utils/views.py b/src/newsreader/utils/views.py index 8325aa5..60f00ef 100644 --- a/src/newsreader/utils/views.py +++ b/src/newsreader/utils/views.py @@ -1,29 +1 @@ -from django.urls import reverse -from django.views.generic.base import ContextMixin - - -# TODO: render menu in non-homepage pages -class NavListMixin(ContextMixin): - def get_context_data(self, **kwargs: dict): - context_data = super().get_context_data(**kwargs) - - authenticated_links = { - "Home": reverse("index"), - "Categories": reverse("news:core:categories"), - "Sources": reverse("news:collection:rules"), - "Settings": reverse("accounts:settings:home"), - } - - if self.request.user.is_authenticated: - authenticated_links["Admin"] = reverse("admin:index") - - authenticated_links["Logout"] = reverse("accounts:logout") - - unauthenticated_links = { - "Login": reverse("accounts:login"), - } - - if self.request.user.is_authenticated: - return {**context_data, "sidebar_links": authenticated_links} - - return {**context_data, "sidebar_links": unauthenticated_links} +# Create your views here. diff --git a/uv.lock b/uv.lock deleted file mode 100644 index d357490..0000000 --- a/uv.lock +++ /dev/null @@ -1,813 +0,0 @@ -version = 1 -revision = 2 -requires-python = ">=3.11" -resolution-markers = [ - "sys_platform == 'linux'", -] -supported-markers = [ - "sys_platform == 'linux'", -] - -[[package]] -name = "amqp" -version = "5.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "vine", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/2c/6eb09fbdeb3c060b37bd33f8873832897a83e7a428afe01aad333fc405ec/amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd", size = 128754, upload-time = "2023-11-06T04:54:20.099Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/f0/8e5be5d5e0653d9e1d02b1144efa33ff7d2963dfad07049e02c0fa9b2e8d/amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637", size = 50917, upload-time = "2023-11-06T04:54:08.603Z" }, -] - -[[package]] -name = "asgiref" -version = "3.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186, upload-time = "2024-03-22T14:39:36.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.12.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181, upload-time = "2024-01-17T16:53:17.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925, upload-time = "2024-01-17T16:53:12.779Z" }, -] - -[[package]] -name = "billiard" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/52/f10d74fd56e73b430c37417658158ad8386202b069b70ff97d945c3ab67a/billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c", size = 154665, upload-time = "2023-11-06T05:23:38.562Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/8d/6e9fdeeab04d803abc5a715175f87e88893934d5590595eacff23ca12b07/billiard-4.2.0-py3-none-any.whl", hash = "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d", size = 86720, upload-time = "2023-11-06T05:23:29.122Z" }, -] - -[[package]] -name = "bleach" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six", marker = "sys_platform == 'linux'" }, - { name = "webencodings", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/10/77f32b088738f40d4f5be801daa5f327879eadd4562f36a2b5ab975ae571/bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe", size = 202119, upload-time = "2023-10-06T19:30:51.304Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/63/da7237f805089ecc28a3f36bca6a21c31fcbc2eb380f3b8f1be3312abd14/bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6", size = 162750, upload-time = "2023-10-06T19:30:49.408Z" }, -] - -[[package]] -name = "celery" -version = "5.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "billiard", marker = "sys_platform == 'linux'" }, - { name = "click", marker = "sys_platform == 'linux'" }, - { name = "click-didyoumean", marker = "sys_platform == 'linux'" }, - { name = "click-plugins", marker = "sys_platform == 'linux'" }, - { name = "click-repl", marker = "sys_platform == 'linux'" }, - { name = "kombu", marker = "sys_platform == 'linux'" }, - { name = "python-dateutil", marker = "sys_platform == 'linux'" }, - { name = "tzdata", marker = "sys_platform == 'linux'" }, - { name = "vine", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/9c/cf0bce2cc1c8971bf56629d8f180e4ca35612c7e79e6e432e785261a8be4/celery-5.4.0.tar.gz", hash = "sha256:504a19140e8d3029d5acad88330c541d4c3f64c789d85f94756762d8bca7e706", size = 1575692, upload-time = "2024-04-17T20:29:43.675Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/c4/6a4d3772e5407622feb93dd25c86ce3c0fee746fa822a777a627d56b4f2a/celery-5.4.0-py3-none-any.whl", hash = "sha256:369631eb580cf8c51a82721ec538684994f8277637edde2dfc0dacd73ed97f64", size = 425983, upload-time = "2024-04-17T20:29:39.406Z" }, -] - -[[package]] -name = "certifi" -version = "2024.7.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/02/a95f2b11e207f68bc64d7aae9666fed2e2b3f307748d5123dffb72a1bbea/certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", size = 164065, upload-time = "2024-07-04T01:36:11.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90", size = 162960, upload-time = "2024-07-04T01:36:09.038Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809, upload-time = "2023-11-01T04:04:59.997Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582, upload-time = "2023-11-01T04:02:59.776Z" }, - { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645, upload-time = "2023-11-01T04:03:02.186Z" }, - { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398, upload-time = "2023-11-01T04:03:04.255Z" }, - { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273, upload-time = "2023-11-01T04:03:05.983Z" }, - { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577, upload-time = "2023-11-01T04:03:07.567Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747, upload-time = "2023-11-01T04:03:08.886Z" }, - { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375, upload-time = "2023-11-01T04:03:10.613Z" }, - { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474, upload-time = "2023-11-01T04:03:11.973Z" }, - { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232, upload-time = "2023-11-01T04:03:13.505Z" }, - { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859, upload-time = "2023-11-01T04:03:17.362Z" }, - { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275, upload-time = "2023-11-01T04:03:28.466Z" }, - { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518, upload-time = "2023-11-01T04:03:29.82Z" }, - { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182, upload-time = "2023-11-01T04:03:31.511Z" }, - { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869, upload-time = "2023-11-01T04:03:32.887Z" }, - { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042, upload-time = "2023-11-01T04:03:34.412Z" }, - { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275, upload-time = "2023-11-01T04:03:35.759Z" }, - { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819, upload-time = "2023-11-01T04:03:37.216Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415, upload-time = "2023-11-01T04:03:38.694Z" }, - { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212, upload-time = "2023-11-01T04:03:40.07Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167, upload-time = "2023-11-01T04:03:41.491Z" }, - { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543, upload-time = "2023-11-01T04:04:58.622Z" }, -] - -[[package]] -name = "click" -version = "8.1.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121, upload-time = "2023-08-17T17:29:11.868Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941, upload-time = "2023-08-17T17:29:10.08Z" }, -] - -[[package]] -name = "click-didyoumean" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/ce/217289b77c590ea1e7c24242d9ddd6e249e52c795ff10fac2c50062c48cb/click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463", size = 3089, upload-time = "2024-03-24T08:22:07.499Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/5b/974430b5ffdb7a4f1941d13d83c64a0395114503cc357c6b9ae4ce5047ed/click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c", size = 3631, upload-time = "2024-03-24T08:22:06.356Z" }, -] - -[[package]] -name = "click-plugins" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/1d/45434f64ed749540af821fd7e42b8e4d23ac04b1eda7c26613288d6cd8a8/click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", size = 8164, upload-time = "2019-04-04T04:27:04.82Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/da/824b92d9942f4e472702488857914bdd50f73021efea15b4cad9aca8ecef/click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8", size = 7497, upload-time = "2019-04-04T04:27:03.36Z" }, -] - -[[package]] -name = "click-repl" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click", marker = "sys_platform == 'linux'" }, - { name = "prompt-toolkit", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/a2/57f4ac79838cfae6912f997b4d1a64a858fb0c86d7fcaae6f7b58d267fca/click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9", size = 10449, upload-time = "2023-06-15T12:43:51.141Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812", size = 10289, upload-time = "2023-06-15T12:43:48.626Z" }, -] - -[[package]] -name = "coverage" -version = "7.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791, upload-time = "2024-08-04T19:45:30.9Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279, upload-time = "2024-08-04T19:43:33.581Z" }, - { url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859, upload-time = "2024-08-04T19:43:35.301Z" }, - { url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549, upload-time = "2024-08-04T19:43:37.578Z" }, - { url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477, upload-time = "2024-08-04T19:43:39.92Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134, upload-time = "2024-08-04T19:43:41.453Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910, upload-time = "2024-08-04T19:43:43.037Z" }, - { url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342, upload-time = "2024-08-04T19:43:53.746Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371, upload-time = "2024-08-04T19:43:55.993Z" }, - { url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455, upload-time = "2024-08-04T19:43:57.618Z" }, - { url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924, upload-time = "2024-08-04T19:44:00.012Z" }, - { url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252, upload-time = "2024-08-04T19:44:01.713Z" }, - { url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897, upload-time = "2024-08-04T19:44:03.898Z" }, - { url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886, upload-time = "2024-08-04T19:44:12.83Z" }, - { url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037, upload-time = "2024-08-04T19:44:15.393Z" }, - { url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038, upload-time = "2024-08-04T19:44:17.466Z" }, - { url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690, upload-time = "2024-08-04T19:44:19.336Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765, upload-time = "2024-08-04T19:44:20.994Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611, upload-time = "2024-08-04T19:44:22.616Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839, upload-time = "2024-08-04T19:44:32.412Z" }, - { url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569, upload-time = "2024-08-04T19:44:34.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927, upload-time = "2024-08-04T19:44:36.313Z" }, - { url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401, upload-time = "2024-08-04T19:44:38.155Z" }, - { url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301, upload-time = "2024-08-04T19:44:39.883Z" }, - { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598, upload-time = "2024-08-04T19:44:41.59Z" }, -] - -[[package]] -name = "cron-descriptor" -version = "1.4.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/83/70bd410dc6965e33a5460b7da84cf0c5a7330a68d6d5d4c3dfdb72ca117e/cron_descriptor-1.4.5.tar.gz", hash = "sha256:f51ce4ffc1d1f2816939add8524f206c376a42c87a5fca3091ce26725b3b1bca", size = 30666, upload-time = "2024-08-24T18:16:48.654Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/20/2cfe598ead23a715a00beb716477cfddd3e5948cf203c372d02221e5b0c6/cron_descriptor-1.4.5-py3-none-any.whl", hash = "sha256:736b3ae9d1a99bc3dbfc5b55b5e6e7c12031e7ba5de716625772f8b02dcd6013", size = 50370, upload-time = "2024-08-24T18:16:46.783Z" }, -] - -[[package]] -name = "django" -version = "4.2.16" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asgiref", marker = "sys_platform == 'linux'" }, - { name = "sqlparse", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/d8/a607ee443b54a4db4ad28902328b906ae6218aa556fb9b3ac45c0bcb313d/Django-4.2.16.tar.gz", hash = "sha256:6f1616c2786c408ce86ab7e10f792b8f15742f7b7b7460243929cb371e7f1dad", size = 10436023, upload-time = "2024-09-03T14:02:02.046Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/2c/6b6c7e493d5ea789416918658ebfa16be7a64c77610307497ed09a93c8c4/Django-4.2.16-py3-none-any.whl", hash = "sha256:1ddc333a16fc139fd253035a1606bb24261951bbc3a6ca256717fa06cc41a898", size = 7992936, upload-time = "2024-09-03T14:01:55.363Z" }, -] - -[[package]] -name = "django-axes" -version = "6.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asgiref", marker = "sys_platform == 'linux'" }, - { name = "django", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6e/a5/31721dc9777fe7f01b4bd710f93d031ff03603b960bc282c53edd5578bf2/django_axes-6.5.1.tar.gz", hash = "sha256:d57f0fc95d581a602c642b3fe5bc31488b9401bd7441f3bec1fef0e599028499", size = 246679, upload-time = "2024-07-01T14:36:45.168Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/39/316e4b5a4c931698480953ea5f43df0657f8c47b9e981cfc331b8ed9eef5/django_axes-6.5.1-py3-none-any.whl", hash = "sha256:7435068cc8523bfa3f34faa62bb3a772b76d00925c3ff54aef43e4316e74bf05", size = 68409, upload-time = "2024-07-01T14:36:30.174Z" }, -] - -[[package]] -name = "django-celery-beat" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "celery", marker = "sys_platform == 'linux'" }, - { name = "cron-descriptor", marker = "sys_platform == 'linux'" }, - { name = "django", marker = "sys_platform == 'linux'" }, - { name = "django-timezone-field", marker = "sys_platform == 'linux'" }, - { name = "python-crontab", marker = "sys_platform == 'linux'" }, - { name = "tzdata", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/03/8f/8a18f234173001bd7a7d63826d2d7f456b38031c892514d27c0f7aea10be/django_celery_beat-2.7.0.tar.gz", hash = "sha256:8482034925e09b698c05ad61c36ed2a8dbc436724a3fe119215193a4ca6dc967", size = 163472, upload-time = "2024-08-22T11:12:24.943Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/f8/f5a25472222b19258c3a53ce71c4efd171a12ab3c988bb3026dec0522a64/django_celery_beat-2.7.0-py3-none-any.whl", hash = "sha256:851c680d8fbf608ca5fecd5836622beea89fa017bc2b3f94a5b8c648c32d84b1", size = 94097, upload-time = "2024-08-22T11:12:23.172Z" }, -] - -[[package]] -name = "django-debug-toolbar" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django", marker = "sys_platform == 'linux'" }, - { name = "sqlparse", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/ff/b6d3cc2c31f9a6cf68eda0f7a640ada743f5c39122a0c14db8d3eee3f412/django_debug_toolbar-4.3.0.tar.gz", hash = "sha256:0b0dddee5ea29b9cb678593bc0d7a6d76b21d7799cb68e091a2148341a80f3c4", size = 261173, upload-time = "2024-02-01T19:36:52.509Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/0e/73d81b1dfd9a84f24e3869019309758b37a5a5a9fe2bc7e54fca08191ea0/django_debug_toolbar-4.3.0-py3-none-any.whl", hash = "sha256:e09b7dcb8417b743234dfc57c95a7c1d1d87a88844abd13b4c5387f807b31bf6", size = 223656, upload-time = "2024-02-01T19:37:22.036Z" }, -] - -[[package]] -name = "django-extensions" -version = "3.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/f1/318684c9466968bf9a9c221663128206e460c1a67f595055be4b284cde8a/django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a", size = 277216, upload-time = "2023-06-05T17:09:01.447Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/7e/ba12b9660642663f5273141018d2bec0a1cae1711f4f6d1093920e157946/django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401", size = 229868, upload-time = "2023-06-05T17:08:58.197Z" }, -] - -[[package]] -name = "django-rest-framework" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "djangorestframework", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/d2/61159bc6efd1bf16adc4a2a48f7ace2080d1f7aef054f606d1857cab490c/django-rest-framework-0.1.0.tar.gz", hash = "sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a", size = 969, upload-time = "2017-07-20T17:14:33.345Z" } - -[[package]] -name = "django-stubs" -version = "5.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asgiref", marker = "sys_platform == 'linux'" }, - { name = "django", marker = "sys_platform == 'linux'" }, - { name = "django-stubs-ext", marker = "sys_platform == 'linux'" }, - { name = "types-pyyaml", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/f4/2dfc77809e4d04164ec614755e2359ec2e68a32f7b5428909fa0b7f8f4e0/django_stubs-5.0.4.tar.gz", hash = "sha256:78e3764488fdfd2695f12502136548ec22f8d4b1780541a835042b8238d11514", size = 262238, upload-time = "2024-07-28T18:10:22.041Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/f0/36c0f82ed7b4ef630b39e165590645c4fe4361f52d41bca5001327d62f57/django_stubs-5.0.4-py3-none-any.whl", hash = "sha256:c2502f5ecbae50c68f9a86d52b5b2447d8648fd205036dad0ccb41e19a445927", size = 466530, upload-time = "2024-07-28T18:10:19.978Z" }, -] - -[[package]] -name = "django-stubs-ext" -version = "5.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/77/ef453a8286fff87db8efd7fe93c1a86f05aeddcc78973c883af91b667f74/django_stubs_ext-5.0.4.tar.gz", hash = "sha256:85da065224204774208be29c7d02b4482d5a69218a728465c2fbe41725fdc819", size = 9410, upload-time = "2024-07-28T18:09:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/54/66a7ccb1f4e4a8e37e0881a3dfdcabaee9fc0c0c91cbe64170e794acebd7/django_stubs_ext-5.0.4-py3-none-any.whl", hash = "sha256:910cbaff3d1e8e806a5c27d5ddd4088535aae8371ea921b7fd680fdfa5f14e30", size = 8954, upload-time = "2024-07-28T18:09:41.325Z" }, -] - -[[package]] -name = "django-timezone-field" -version = "7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/b3/992aa517b95f2e6934aa05b8160cf55f91c49c7b91e33076ea9af2f29920/django_timezone_field-7.0.tar.gz", hash = "sha256:aa6f4965838484317b7f08d22c0d91a53d64e7bbbd34264468ae83d4023898a7", size = 13683, upload-time = "2024-07-07T18:32:06.941Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/f9/11769c4414026f1a9ce3e581731d07b084683fc7b4c580703dc71ef81347/django_timezone_field-7.0-py3-none-any.whl", hash = "sha256:3232e7ecde66ba4464abb6f9e6b8cc739b914efb9b29dc2cf2eee451f7cc2acb", size = 13161, upload-time = "2024-07-07T18:32:04.639Z" }, -] - -[[package]] -name = "djangorestframework" -version = "3.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/60/cc2dd985400293fe7bf3fa1b9a5d61f5b44200c33f7d31952f2c9fd79e8a/djangorestframework-3.15.1.tar.gz", hash = "sha256:f88fad74183dfc7144b2756d0d2ac716ea5b4c7c9840995ac3bfd8ec034333c1", size = 1066194, upload-time = "2024-03-22T15:50:51.875Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/7e/8c45ea7f85dd5d52ceddbacc6f56ecaca21ecbfc0e8c34c95618a14d5082/djangorestframework-3.15.1-py3-none-any.whl", hash = "sha256:3ccc0475bce968608cf30d07fb17d8e52d1d7fc8bfe779c905463200750cbca6", size = 1067096, upload-time = "2024-03-22T15:50:48.759Z" }, -] - -[[package]] -name = "djangorestframework-camel-case" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/87/647ce93053cb5e35e07bded676340774fe43190388b885c54aff47d8557b/djangorestframework-camel-case-1.4.2.tar.gz", hash = "sha256:cdae75846648abb6585c7470639a1d2fb064dc45f8e8b62aaa50be7f1a7a61f4", size = 8839, upload-time = "2023-02-13T15:28:11.941Z" } - -[[package]] -name = "factory-boy" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "faker", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146, upload-time = "2025-02-03T09:49:04.433Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036, upload-time = "2025-02-03T09:49:01.659Z" }, -] - -[[package]] -name = "faker" -version = "37.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tzdata", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/37/62/80f15fe1b5abf3e5b09815178d7eb63a150fc7fcfebd5271ca4aab1d885a/faker-37.0.2.tar.gz", hash = "sha256:948bd27706478d3aa0b6f9f58b9f25207098f6ca79852c7b49c44a8ced2bc59b", size = 1875441, upload-time = "2025-03-19T15:29:47.141Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/8b/b738d3d79ee4502ca966a2a4fa6833c11f50130127bdd57729e9b29c6d2f/faker-37.0.2-py3-none-any.whl", hash = "sha256:8955706c56c28099585e9e2b6f814eb0a3a227eb36a2ee3eb9ab577c4764eacc", size = 1918397, upload-time = "2025-03-19T15:29:43.885Z" }, -] - -[[package]] -name = "feedparser" -version = "6.0.11" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sgmllib3k", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/aa/7af346ebeb42a76bf108027fe7f3328bb4e57a3a96e53e21fd9ef9dd6dd0/feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5", size = 286197, upload-time = "2023-12-10T16:03:20.854Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/d4/8c31aad9cc18f451c49f7f9cfb5799dadffc88177f7917bc90a66459b1d7/feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45", size = 81343, upload-time = "2023-12-10T16:03:19.484Z" }, -] - -[[package]] -name = "freezegun" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/ef/722b8d71ddf4d48f25f6d78aa2533d505bf3eec000a7cacb8ccc8de61f2f/freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9", size = 33697, upload-time = "2024-05-11T17:32:53.911Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/0b/0d7fee5919bccc1fdc1c2a7528b98f65c6f69b223a3fd8f809918c142c36/freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1", size = 17569, upload-time = "2024-05-11T17:32:51.715Z" }, -] - -[[package]] -name = "ftfy" -version = "6.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/a9/59f4354257e8350a25be1774021991fb3a99a2fb87d0c1f367592548aed3/ftfy-6.2.3.tar.gz", hash = "sha256:79b505988f29d577a58a9069afe75553a02a46e42de6091c0660cdc67812badc", size = 64165, upload-time = "2024-08-06T01:30:46.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/46/14d230ad057048aea7ccd2f96a80905830866d281ea90a6662a825490659/ftfy-6.2.3-py3-none-any.whl", hash = "sha256:f15761b023f3061a66207d33f0c0149ad40a8319fd16da91796363e2c049fdf8", size = 43011, upload-time = "2024-08-06T01:30:44.955Z" }, -] - -[[package]] -name = "gunicorn" -version = "23.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, -] - -[[package]] -name = "idna" -version = "3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/e349c5e6d4543326c6883ee9491e3921e0d07b55fdf3cce184b40d63e72a/idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603", size = 189467, upload-time = "2024-08-23T16:01:51.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/7e/d71db821f177828df9dea8c42ac46473366f191be53080e552e628aad991/idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", size = 66894, upload-time = "2024-08-23T16:01:49.963Z" }, -] - -[[package]] -name = "kombu" -version = "5.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "amqp", marker = "sys_platform == 'linux'" }, - { name = "vine", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b6/f4/d3e57b1c351bb47ce25b16e1cf6ea05df4613dbe56e3cf32ea80df1a8b4d/kombu-5.4.0.tar.gz", hash = "sha256:ad200a8dbdaaa2bbc5f26d2ee7d707d9a1fded353a0f4bd751ce8c7d9f449c60", size = 442120, upload-time = "2024-08-06T13:42:58.842Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/17/34f8ec5b9d46a1ddb598b7bf8f779c567421d05cd73742d09e549254c782/kombu-5.4.0-py3-none-any.whl", hash = "sha256:c8dd99820467610b4febbc7a9e8a0d3d7da2d35116b67184418b51cc520ea6b6", size = 200870, upload-time = "2024-08-06T13:42:55.53Z" }, -] - -[[package]] -name = "lxml" -version = "5.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318, upload-time = "2024-08-10T18:17:29.668Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197, upload-time = "2024-08-10T18:10:16.825Z" }, - { url = "https://files.pythonhosted.org/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809, upload-time = "2024-08-10T18:10:20.046Z" }, - { url = "https://files.pythonhosted.org/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593, upload-time = "2024-08-10T18:10:23.641Z" }, - { url = "https://files.pythonhosted.org/packages/23/0e/caac672ec246d3189a16c4d364ed4f7d6bf856c080215382c06764058c08/lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e", size = 4866657, upload-time = "2024-08-10T18:10:26.528Z" }, - { url = "https://files.pythonhosted.org/packages/67/a4/1f5fbd3f58d4069000522196b0b776a014f3feec1796da03e495cf23532d/lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c", size = 4967017, upload-time = "2024-08-10T18:10:29.639Z" }, - { url = "https://files.pythonhosted.org/packages/ee/73/623ecea6ca3c530dd0a4ed0d00d9702e0e85cd5624e2d5b93b005fe00abd/lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16", size = 4810730, upload-time = "2024-08-10T18:10:33.387Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/fb84fb8e3c298f3a245ae3ea6221c2426f1bbaa82d10a88787412a498145/lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79", size = 5455154, upload-time = "2024-08-10T18:10:36.897Z" }, - { url = "https://files.pythonhosted.org/packages/b1/72/4d1ad363748a72c7c0411c28be2b0dc7150d91e823eadad3b91a4514cbea/lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080", size = 4969416, upload-time = "2024-08-10T18:10:40.331Z" }, - { url = "https://files.pythonhosted.org/packages/42/07/b29571a58a3a80681722ea8ed0ba569211d9bb8531ad49b5cacf6d409185/lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654", size = 5013672, upload-time = "2024-08-10T18:10:43.768Z" }, - { url = "https://files.pythonhosted.org/packages/b9/93/bde740d5a58cf04cbd38e3dd93ad1e36c2f95553bbf7d57807bc6815d926/lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d", size = 4878644, upload-time = "2024-08-10T18:10:47.901Z" }, - { url = "https://files.pythonhosted.org/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531, upload-time = "2024-08-10T18:10:51.581Z" }, - { url = "https://files.pythonhosted.org/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065, upload-time = "2024-08-10T18:10:54.841Z" }, - { url = "https://files.pythonhosted.org/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775, upload-time = "2024-08-10T18:10:57.808Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778, upload-time = "2024-08-10T18:11:16.233Z" }, - { url = "https://files.pythonhosted.org/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628, upload-time = "2024-08-10T18:11:19.507Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215, upload-time = "2024-08-10T18:11:23.708Z" }, - { url = "https://files.pythonhosted.org/packages/99/37/396fbd24a70f62b31d988e4500f2068c7f3fd399d2fd45257d13eab51a6f/lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a", size = 4813963, upload-time = "2024-08-10T18:11:26.997Z" }, - { url = "https://files.pythonhosted.org/packages/09/91/e6136f17459a11ce1757df864b213efbeab7adcb2efa63efb1b846ab6723/lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3", size = 4923353, upload-time = "2024-08-10T18:11:30.478Z" }, - { url = "https://files.pythonhosted.org/packages/1d/7c/2eeecf87c9a1fca4f84f991067c693e67340f2b7127fc3eca8fa29d75ee3/lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1", size = 4740541, upload-time = "2024-08-10T18:11:34.344Z" }, - { url = "https://files.pythonhosted.org/packages/3b/ed/4c38ba58defca84f5f0d0ac2480fdcd99fc7ae4b28fc417c93640a6949ae/lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d", size = 5346504, upload-time = "2024-08-10T18:11:37.595Z" }, - { url = "https://files.pythonhosted.org/packages/a5/22/bbd3995437e5745cb4c2b5d89088d70ab19d4feabf8a27a24cecb9745464/lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c", size = 4898077, upload-time = "2024-08-10T18:11:40.867Z" }, - { url = "https://files.pythonhosted.org/packages/0a/6e/94537acfb5b8f18235d13186d247bca478fea5e87d224644e0fe907df976/lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99", size = 4946543, upload-time = "2024-08-10T18:11:44.954Z" }, - { url = "https://files.pythonhosted.org/packages/8d/e8/4b15df533fe8e8d53363b23a41df9be907330e1fa28c7ca36893fad338ee/lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff", size = 4816841, upload-time = "2024-08-10T18:11:49.046Z" }, - { url = "https://files.pythonhosted.org/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341, upload-time = "2024-08-10T18:11:52.295Z" }, - { url = "https://files.pythonhosted.org/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539, upload-time = "2024-08-10T18:11:55.98Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542, upload-time = "2024-08-10T18:11:59.351Z" }, - { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331, upload-time = "2024-08-10T18:12:17.204Z" }, - { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835, upload-time = "2024-08-10T18:12:21.172Z" }, - { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649, upload-time = "2024-08-10T18:12:24.897Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046, upload-time = "2024-08-10T18:12:29.028Z" }, - { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597, upload-time = "2024-08-10T18:12:32.278Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071, upload-time = "2024-08-10T18:12:35.407Z" }, - { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213, upload-time = "2024-08-10T18:12:38.73Z" }, - { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749, upload-time = "2024-08-10T18:12:42.606Z" }, - { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901, upload-time = "2024-08-10T18:12:45.944Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447, upload-time = "2024-08-10T18:12:49.051Z" }, - { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186, upload-time = "2024-08-10T18:12:52.388Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481, upload-time = "2024-08-10T18:12:56.021Z" }, - { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053, upload-time = "2024-08-10T18:12:59.714Z" }, -] - -[[package]] -name = "newsreader" -version = "0.5.3" -source = { virtual = "." } -dependencies = [ - { name = "beautifulsoup4", marker = "sys_platform == 'linux'" }, - { name = "bleach", marker = "sys_platform == 'linux'" }, - { name = "celery", marker = "sys_platform == 'linux'" }, - { name = "django", marker = "sys_platform == 'linux'" }, - { name = "django-axes", marker = "sys_platform == 'linux'" }, - { name = "django-celery-beat", marker = "sys_platform == 'linux'" }, - { name = "django-rest-framework", marker = "sys_platform == 'linux'" }, - { name = "djangorestframework-camel-case", marker = "sys_platform == 'linux'" }, - { name = "feedparser", marker = "sys_platform == 'linux'" }, - { name = "ftfy", marker = "sys_platform == 'linux'" }, - { name = "lxml", marker = "sys_platform == 'linux'" }, - { name = "psycopg", extra = ["binary"], marker = "sys_platform == 'linux'" }, - { name = "pymemcache", marker = "sys_platform == 'linux'" }, - { name = "python-dotenv", marker = "sys_platform == 'linux'" }, - { name = "requests", marker = "sys_platform == 'linux'" }, -] - -[package.optional-dependencies] -sentry = [ - { name = "sentry-sdk", marker = "sys_platform == 'linux'" }, -] - -[package.dev-dependencies] -ci = [ - { name = "coverage", marker = "sys_platform == 'linux'" }, -] -development = [ - { name = "django-debug-toolbar", marker = "sys_platform == 'linux'" }, - { name = "django-extensions", marker = "sys_platform == 'linux'" }, - { name = "django-stubs", marker = "sys_platform == 'linux'" }, -] -production = [ - { name = "gunicorn", marker = "sys_platform == 'linux'" }, -] -test-tools = [ - { name = "factory-boy", marker = "sys_platform == 'linux'" }, - { name = "freezegun", marker = "sys_platform == 'linux'" }, - { name = "ruff", marker = "sys_platform == 'linux'" }, -] - -[package.metadata] -requires-dist = [ - { name = "beautifulsoup4" }, - { name = "bleach" }, - { name = "celery", specifier = "~=5.4" }, - { name = "django", specifier = "~=4.2" }, - { name = "django-axes" }, - { name = "django-celery-beat", specifier = "~=2.7.0" }, - { name = "django-rest-framework" }, - { name = "djangorestframework-camel-case" }, - { name = "feedparser" }, - { name = "ftfy", specifier = "~=6.2" }, - { name = "lxml" }, - { name = "psycopg", extras = ["binary"] }, - { name = "pymemcache" }, - { name = "python-dotenv", specifier = "~=1.0.1" }, - { name = "requests" }, - { name = "sentry-sdk", marker = "extra == 'sentry'", specifier = "~=2.0" }, -] -provides-extras = ["sentry"] - -[package.metadata.requires-dev] -ci = [{ name = "coverage", specifier = "~=7.6.1" }] -development = [ - { name = "django-debug-toolbar" }, - { name = "django-extensions" }, - { name = "django-stubs" }, -] -production = [{ name = "gunicorn", specifier = "~=23.0" }] -test-tools = [ - { name = "factory-boy" }, - { name = "freezegun" }, - { name = "ruff" }, -] - -[[package]] -name = "packaging" -version = "24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788, upload-time = "2024-06-09T23:19:24.956Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985, upload-time = "2024-06-09T23:19:21.909Z" }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.47" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/47/6d/0279b119dafc74c1220420028d490c4399b790fc1256998666e3a341879f/prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360", size = 425859, upload-time = "2024-06-10T11:02:14.045Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/23/22750c4b768f09386d1c3cc4337953e8936f48a888fa6dddfb669b2c9088/prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10", size = 386411, upload-time = "2024-06-10T11:02:10.477Z" }, -] - -[[package]] -name = "psycopg" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/8e/f176997fd790d3dce9fa0ca695391beaeee39af7ecd6d426c4c063cf6744/psycopg-3.2.1.tar.gz", hash = "sha256:dc8da6dc8729dacacda3cc2f17d2c9397a70a66cf0d2b69c91065d60d5f00cb7", size = 155313, upload-time = "2024-07-01T03:35:50.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/0f755db36f47f96464463385552f8f132a981731356837c9a30a11ab2d35/psycopg-3.2.1-py3-none-any.whl", hash = "sha256:ece385fb413a37db332f97c49208b36cf030ff02b199d7635ed2fbd378724175", size = 197743, upload-time = "2024-07-01T03:30:14.942Z" }, -] - -[package.optional-dependencies] -binary = [ - { name = "psycopg-binary", marker = "implementation_name != 'pypy' and sys_platform == 'linux'" }, -] - -[[package]] -name = "psycopg-binary" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/2a/d45ff1f4b8d5b334695f3f5a68c722dbf483b65348f2e2639cf2f45c7b73/psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40bb515d042f6a345714ec0403df68ccf13f73b05e567837d80c886c7c9d3805", size = 4464849, upload-time = "2024-07-01T03:31:47.787Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/60562887f1363747ce2e074841548f96b433dd50e78d822c88e7ad6ec817/psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6418712ba63cebb0c88c050b3997185b0ef54173b36568522d5634ac06153040", size = 4263085, upload-time = "2024-07-01T03:31:55.249Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4f/af3cb85b967d2616c9c4e2bea9e865c8d0c38fc83ce5db1ef050ceba2bea/psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:101472468d59c74bb8565fab603e032803fd533d16be4b2d13da1bab8deb32a3", size = 4514411, upload-time = "2024-07-01T03:31:59.738Z" }, - { url = "https://files.pythonhosted.org/packages/1d/00/685055d15f70e57d24cffe59021d53d428cdd7126b87442b5b07c9ffd222/psycopg_binary-3.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa3931f308ab4a479d0ee22dc04bea867a6365cac0172e5ddcba359da043854b", size = 4207636, upload-time = "2024-07-01T03:32:04.407Z" }, - { url = "https://files.pythonhosted.org/packages/72/9f/d6f6c8f60c4ebcc270efda17ab22110b24934f610dc7d5d3e2dc1e9eecbc/psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dc314a47d44fe1a8069b075a64abffad347a3a1d8652fed1bab5d3baea37acb2", size = 3132484, upload-time = "2024-07-01T03:32:09.851Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/742cca374ab3725606f79a9b3b2429bba73917e1d14d52ba39d83dec0a3c/psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cc304a46be1e291031148d9d95c12451ffe783ff0cc72f18e2cc7ec43cdb8c68", size = 3111128, upload-time = "2024-07-01T03:32:16.741Z" }, - { url = "https://files.pythonhosted.org/packages/61/a9/046536ef56a785e12c72c2a2507058473889bd7d625fbce142f1a1662bc2/psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f9e13600647087df5928875559f0eb8f496f53e6278b7da9511b4b3d0aff960", size = 3213088, upload-time = "2024-07-01T03:32:22.139Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/a988739a5d8e72c553a44abba71217c601400e5164a874916e2aa4285139/psycopg_binary-3.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b140182830c76c74d17eba27df3755a46442ce8d4fb299e7f1cf2f74a87c877b", size = 3252404, upload-time = "2024-07-01T03:32:29.682Z" }, - { url = "https://files.pythonhosted.org/packages/3f/9a/28da916a65fb40fb3e1a97e1ae0a26860d8c1265c6e9766bd6c47abc437b/psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e8213bf50af073b1aa8dc3cff123bfeedac86332a16c1b7274910bc88a847c7", size = 4443593, upload-time = "2024-07-01T03:32:54.341Z" }, - { url = "https://files.pythonhosted.org/packages/b0/9a/3dc1237a2ef3344b347af79e1aad2a60277cfafa2846f54cb13e1cd8c528/psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74d623261655a169bc84a9669890975c229f2fa6e19a7f2d10a77675dcf1a707", size = 4247005, upload-time = "2024-07-01T03:33:01.162Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a9/06491cb0338b6f0868d349d2a526586dc165e508b64daa2ff45f9db7ba4b/psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42781ba94e8842ee98bca5a7d0c44cc9d067500fedca2d6a90fa3609b6d16b42", size = 4484179, upload-time = "2024-07-01T03:33:09.385Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5f/b1116467dd18b4efc1aa7f03c96da751724a43c6a630979c61f60a9fbe5f/psycopg_binary-3.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e6669091d09f8ba36e10ce678a6d9916e110446236a9b92346464a3565635e", size = 4186490, upload-time = "2024-07-01T03:33:15.295Z" }, - { url = "https://files.pythonhosted.org/packages/a4/87/6092d1701d36c5aeb74c35cb54266fd44ee0f7711cafa4c0bffd873bdb61/psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b09e8a576a2ac69d695032ee76f31e03b30781828b5dd6d18c6a009e5a3d1c35", size = 3109385, upload-time = "2024-07-01T03:33:20.174Z" }, - { url = "https://files.pythonhosted.org/packages/62/61/4ad7e29d09202478b6f568fff19efa978a4f2c25cb5efcd73544a4ee8be7/psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8f28ff0cb9f1defdc4a6f8c958bf6787274247e7dfeca811f6e2f56602695fb1", size = 3094397, upload-time = "2024-07-01T03:33:25.285Z" }, - { url = "https://files.pythonhosted.org/packages/b7/dd/0ae42c64bf524d1fcf9bf861ab09d331e693ae00e527ba08131b2d3729a3/psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4c84fcac8a3a3479ac14673095cc4e1fdba2935499f72c436785ac679bec0d1a", size = 3184097, upload-time = "2024-07-01T03:33:31.268Z" }, - { url = "https://files.pythonhosted.org/packages/dd/f0/09329ebb0cd03e2ee5786fc9914ac904f4965b78627f15826f8258fde734/psycopg_binary-3.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:950fd666ec9e9fe6a8eeb2b5a8f17301790e518953730ad44d715b59ffdbc67f", size = 3228517, upload-time = "2024-07-01T03:33:37.824Z" }, -] - -[[package]] -name = "pymemcache" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/b6/4541b664aeaad025dfb8e851dcddf8e25ab22607e674dd2b562ea3e3586f/pymemcache-4.0.0.tar.gz", hash = "sha256:27bf9bd1bbc1e20f83633208620d56de50f14185055e49504f4f5e94e94aff94", size = 70176, upload-time = "2022-10-17T16:53:07.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/ba/2f7b22d8135b51c4fefb041461f8431e1908778e6539ff5af6eeaaee367a/pymemcache-4.0.0-py2.py3-none-any.whl", hash = "sha256:f507bc20e0dc8d562f8df9d872107a278df049fa496805c1431b926f3ddd0eab", size = 60772, upload-time = "2022-10-17T16:53:04.388Z" }, -] - -[[package]] -name = "python-crontab" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e2/f0/25775565c133d4e29eeb607bf9ddba0075f3af36041a1844dd207881047f/python_crontab-3.2.0.tar.gz", hash = "sha256:40067d1dd39ade3460b2ad8557c7651514cd3851deffff61c5c60e1227c5c36b", size = 57001, upload-time = "2024-07-01T22:29:10.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/91/832fb3b3a1f62bd2ab4924f6be0c7736c9bc4f84d3b153b74efcf6d4e4a1/python_crontab-3.2.0-py3-none-any.whl", hash = "sha256:82cb9b6a312d41ff66fd3caf3eed7115c28c195bfb50711bc2b4b9592feb9fe5", size = 27351, upload-time = "2024-07-01T22:29:08.549Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115, upload-time = "2024-01-23T06:33:00.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" }, -] - -[[package]] -name = "requests" -version = "2.32.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi", marker = "sys_platform == 'linux'" }, - { name = "charset-normalizer", marker = "sys_platform == 'linux'" }, - { name = "idna", marker = "sys_platform == 'linux'" }, - { name = "urllib3", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, -] - -[[package]] -name = "ruff" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5d/f9/0b32e5d1c6f957df49398cd882a011e9488fcbca0d6acfeeea50ccd37a4d/ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983", size = 2463514, upload-time = "2024-08-29T15:16:41.015Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/68/1da6a1e39a03a229ea57c511691d6225072759cc7764206c3f0989521194/ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3", size = 9696928, upload-time = "2024-08-29T15:15:54.085Z" }, - { url = "https://files.pythonhosted.org/packages/a0/20/b0bcb29d4ee437f3567b73b6905c034e2e94d29b9b826c66daecc1cf6388/ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1", size = 10108892, upload-time = "2024-08-29T15:16:01.885Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e3/211bc759f424e8823a9937e0f678695ca02113c621dfde1fa756f9f26f6d/ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672", size = 9476471, upload-time = "2024-08-29T15:16:04.397Z" }, - { url = "https://files.pythonhosted.org/packages/b2/a3/2ec35a2d7a554364864206f0e46812b92a074ad8a014b923d821ead532aa/ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1", size = 10294802, upload-time = "2024-08-29T15:16:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/03/8b/56ef687b3489c88886dea48c78fb4969b6b65f18007d0ac450070edd1f58/ruff-0.6.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384", size = 11022372, upload-time = "2024-08-29T15:16:10.014Z" }, - { url = "https://files.pythonhosted.org/packages/a5/21/327d147feb442adb88975e81e2263102789eba9ad2afa102c661912a482f/ruff-0.6.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a", size = 10596596, upload-time = "2024-08-29T15:16:12.99Z" }, - { url = "https://files.pythonhosted.org/packages/6c/86/ff386de63729da3e08c8099c57f577a00ec9f3eea711b23ac07cf3588dc5/ruff-0.6.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500", size = 11572830, upload-time = "2024-08-29T15:16:15.479Z" }, - { url = "https://files.pythonhosted.org/packages/38/5d/b33284c108e3f315ddd09b70296fd76bd28ecf8965a520bc93f3bbd8ac40/ruff-0.6.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470", size = 10262577, upload-time = "2024-08-29T15:16:18.593Z" }, - { url = "https://files.pythonhosted.org/packages/29/99/9cdfad0d7f460e66567236eddc691473791afd9aff93a0dfcdef0462a6c7/ruff-0.6.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f", size = 10098751, upload-time = "2024-08-29T15:16:21.862Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9f/f801a1619f5549e552f1f722f1db57eb39e7e1d83d482133142781d450de/ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5", size = 9563859, upload-time = "2024-08-29T15:16:24.806Z" }, - { url = "https://files.pythonhosted.org/packages/0b/4d/fb2424faf04ffdb960ae2b3a1d991c5183dd981003de727d2d5cc38abc98/ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351", size = 9914291, upload-time = "2024-08-29T15:16:27.894Z" }, - { url = "https://files.pythonhosted.org/packages/2e/dd/94fddf002a8f6152e8ebfbb51d3f93febc415c1fe694345623c31ce8b33b/ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8", size = 10331549, upload-time = "2024-08-29T15:16:31.045Z" }, -] - -[[package]] -name = "sentry-sdk" -version = "2.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi", marker = "sys_platform == 'linux'" }, - { name = "urllib3", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/41/97f673384dae5ed81cc2a568cc5c28e76deee85f8ba50def862e86150a5a/sentry_sdk-2.13.0.tar.gz", hash = "sha256:8d4a576f7a98eb2fdb40e13106e41f330e5c79d72a68be1316e7852cf4995260", size = 279937, upload-time = "2024-08-13T14:33:34.177Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/7e/e9ca09f24a6c334286631a2d32c267cdc5edad5ac03fd9d20a01a82f1c35/sentry_sdk-2.13.0-py2.py3-none-any.whl", hash = "sha256:6beede8fc2ab4043da7f69d95534e320944690680dd9a963178a49de71d726c6", size = 309078, upload-time = "2024-08-13T14:33:32.262Z" }, -] - -[[package]] -name = "sgmllib3k" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750, upload-time = "2010-08-24T14:33:52.445Z" } - -[[package]] -name = "six" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041, upload-time = "2021-05-05T14:18:18.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053, upload-time = "2021-05-05T14:18:17.237Z" }, -] - -[[package]] -name = "soupsieve" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569, upload-time = "2024-08-13T13:39:12.166Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186, upload-time = "2024-08-13T13:39:10.986Z" }, -] - -[[package]] -name = "sqlparse" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/82/dfa23ec2cbed08a801deab02fe7c904bfb00765256b155941d789a338c68/sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e", size = 84502, upload-time = "2024-07-15T19:30:27.085Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/a5/b2860373aa8de1e626b2bdfdd6df4355f0565b47e51f7d0c54fe70faf8fe/sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4", size = 44156, upload-time = "2024-07-15T19:30:25.033Z" }, -] - -[[package]] -name = "types-pyyaml" -version = "6.0.12.20240808" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dd/08/6f5737f645571b7a0b1ebd2fe8b5cf1ee4ec3e707866ca96042a86fc1d10/types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af", size = 12359, upload-time = "2024-08-08T02:30:32.727Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/ad/ffbad24e2bc8f20bf047ec22af0c0a92f6ce2071eb21c9103df600cda6de/types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35", size = 15298, upload-time = "2024-08-08T02:30:31.101Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321, upload-time = "2024-06-07T18:52:15.995Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" }, -] - -[[package]] -name = "tzdata" -version = "2024.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559, upload-time = "2024-02-11T23:22:40.2Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370, upload-time = "2024-02-11T23:22:38.223Z" }, -] - -[[package]] -name = "urllib3" -version = "2.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/6d/fa469ae21497ddc8bc93e5877702dca7cb8f911e337aca7452b5724f1bb6/urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168", size = 292266, upload-time = "2024-06-17T13:40:11.401Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/1c/89ffc63a9605b583d5df2be791a27bc1a42b7c32bab68d3c8f2f73a98cd4/urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", size = 121444, upload-time = "2024-06-17T13:40:07.795Z" }, -] - -[[package]] -name = "vine" -version = "5.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0", size = 48980, upload-time = "2023-11-05T08:46:53.857Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc", size = 9636, upload-time = "2023-11-05T08:46:51.205Z" }, -] - -[[package]] -name = "wcwidth" -version = "0.2.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, -] - -[[package]] -name = "webencodings" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, -] diff --git a/webpack.common.babel.js b/webpack.common.babel.js index 7cc970c..e9d1386 100644 --- a/webpack.common.babel.js +++ b/webpack.common.babel.js @@ -23,27 +23,23 @@ export default { use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader', 'sass-loader'], }, { - test: /\.(ttf|woff|woff2|eot)$/, - type: 'asset/resource', - generator: { - filename: '[name][ext]', - outputPath: 'fonts', - publicPath: '/fonts/', - }, - }, - { - test: /\.(svg)$/, - type: 'asset/resource', - generator: { - filename: '[name][ext]', - outputPath: 'images', - publicPath: '/fonts/', + test: /\.(ttf|woff|woff2)$/, + use: { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'fonts', + publicPath: '/static/fonts/', + }, }, }, ], }, plugins: [ - new MiniCssExtractPlugin({ filename: 'css/main.css' }), + new MiniCssExtractPlugin({ + filename: 'css/main.css', + allChunks: true, + }), new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['js', 'css', 'fonts'], cleanAfterEveryBuildPatterns: ['!fonts/**'],