diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 610dee0..0000000 --- a/.babelrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "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 deleted file mode 100644 index d1a0d79..0000000 --- a/.coveragerc +++ /dev/null @@ -1,16 +0,0 @@ -[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 new file mode 100644 index 0000000..5257a12 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# 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 754490c..3546fd2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ eggs/ lib/ !src/newsreader/scss/lib +!src/newsreader/js/lib lib64/ parts/ @@ -114,7 +115,7 @@ celerybeat-schedule *.sage.py # Environments -.env +*.env .venv env/ venv/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index beb864f..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index 0c8e37f..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,12 +0,0 @@ -[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 new file mode 100644 index 0000000..b009dfb --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +lts/* diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 146a217..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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 new file mode 100644 index 0000000..519e330 --- /dev/null +++ b/.woodpecker/build.yaml @@ -0,0 +1,10 @@ +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 new file mode 100644 index 0000000..64ee04b --- /dev/null +++ b/.woodpecker/lint.yaml @@ -0,0 +1,18 @@ +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 new file mode 100644 index 0000000..95092f6 --- /dev/null +++ b/.woodpecker/tests.yaml @@ -0,0 +1,37 @@ +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 new file mode 100644 index 0000000..b1341d4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,149 @@ +# 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 + +## 0.3.13.4 + +- Fix import error + +## 0.3.13.3 + +- Use sentry's set_extra to provide extra debug variables + +## 0.3.13.2 + +- Update sentry-sdk + +## 0.3.13.1 + +- Fix mutual exclusive exception for email settings +- Temporarly set exception level for StreamDeniedException exceptions + +## 0.3.13 + +- Update django to 3.2 +- Notify users of expired credentials + +## 0.3.12.1 + +- Add missing background-color + +## 0.3.12 + +- Update light theme +- Sticky navbar +- Sticky post modal header + +## 0.3.11 + +- Add saved posts section +- Bump django version + +## 0.3.10 + +- Add custom color for confirm buttons +- Update font sizes + +## 0.3.9 + +- Cursor based pagination +- Updated django version + +## 0.3.8 + +- Update light / dark theme +- Replace css.gg with fontawesome +- Update deploy job + +## 0.3.7 + +- Add a dark theme +- Update object representations +- Move sentry to optional dependency +- Add CHANGELOG.md + +## 0.3.6.3 + +- Update deploy job + +## 0.3.6.2 + +- Use warning logging level for BuilderSkippedException's +- Change working directory before running ansible + +## 0.3.6.1 + +- Install ansible required roles + +## 0.3.6 + +- Update deploy job +- Add user manageable reddit filters + +## 0.3.5 + +- Show timezone next to post datetimes +- Take read status in consideration when sorting posts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0ffa683 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,84 @@ +# 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 new file mode 100644 index 0000000..ed19be2 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,21 @@ +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 new file mode 100755 index 0000000..bb473e6 --- /dev/null +++ b/bin/docker-entrypoint.sh @@ -0,0 +1,5 @@ +#!/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 new file mode 100644 index 0000000..b63a1ef --- /dev/null +++ b/config/nginx/conf.d/local.conf @@ -0,0 +1,21 @@ +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 new file mode 100644 index 0000000..9045200 --- /dev/null +++ b/docker-compose.development.yml @@ -0,0 +1,36 @@ +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 new file mode 100644 index 0000000..24c8cd1 --- /dev/null +++ b/docker-compose.production.yml @@ -0,0 +1,16 @@ +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 c7dc5ca..c348d96 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,60 +1,126 @@ -version: '3' volumes: + logs: + media: postgres-data: static-files: - node-modules: + +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:-""} services: db: - image: postgres environment: - POSTGRES_DB: "newsreader" - POSTGRES_USER: "newsreader" - POSTGRES_PASSWORD: "newsreader" + <<: *db-env + image: postgres:15 + healthcheck: + test: /usr/bin/pg_isready + start_period: 10s + interval: 5s + timeout: 10s + retries: 10 volumes: - postgres-data:/var/lib/postgresql/data + rabbitmq: - image: rabbitmq:3.7 + image: rabbitmq:4 + memcached: - image: memcached:1.5.22 - ports: - - "11211:11211" + image: memcached:1.6 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: - 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 + <<: *app-build environment: - - DJANGO_SETTINGS_MODULE=newsreader.conf.docker + <<: *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 depends_on: - - rabbitmq + rabbitmq: + condition: service_started + django: + condition: service_healthy volumes: - - .:/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 - 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 + - logs:/app/logs diff --git a/docker/django b/docker/django deleted file mode 100644 index 871828a..0000000 --- a/docker/django +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 6909ee9..0000000 --- a/docker/webpack +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index c8df615..0000000 --- a/gitlab-ci/build.yml +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 07ba824..0000000 --- a/gitlab-ci/deploy.yml +++ /dev/null @@ -1,24 +0,0 @@ -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/sonny/ansible-playbooks.git deployment - - mkdir /root/.ssh - - echo "$DEPLOY_HOST_KEY" > /root/.ssh/known_hosts - - echo "$DEPLOY_KEY" > deployment/deploy_key && chmod 0600 deployment/deploy_key - - mkdir /root/.vaults - - echo "$VAULT_PASSWORD" > /root/.vaults/newsreader && chmod 0600 /root/.vaults/newsreader - script: - - > - ansible-playbook deployment/playbook.yml - --inventory deployment/apps.yml - --limit newsreader - --user ansible - --private-key deployment/deploy_key - --vault-password-file /root/.vaults/newsreader diff --git a/gitlab-ci/lint.yml b/gitlab-ci/lint.yml deleted file mode 100644 index 0300c33..0000000 --- a/gitlab-ci/lint.yml +++ /dev/null @@ -1,28 +0,0 @@ -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 deleted file mode 100644 index d6abaf6..0000000 --- a/gitlab-ci/release.yml +++ /dev/null @@ -1,12 +0,0 @@ -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: 'Auto created release' - tag_name: '$CI_COMMIT_TAG' - ref: '$CI_COMMIT_TAG' diff --git a/gitlab-ci/test.yml b/gitlab-ci/test.yml deleted file mode 100644 index 3114a87..0000000 --- a/gitlab-ci/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 01afeb8..0000000 --- a/jest.config.js +++ /dev/null @@ -1,188 +0,0 @@ -// 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 d884a42..59e4d2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3512 +1,4689 @@ { "name": "newsreader", - "version": "0.1.0", - "lockfileVersion": 1, + "version": "0.5.3", + "lockfileVersion": 3, "requires": true, - "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" - } - }, - "@babel/core": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.7.tgz", - "integrity": "sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.7", - "@babel/helpers": "^7.7.4", - "@babel/parser": "^7.7.7", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, + "packages": { + "": { + "name": "newsreader", + "version": "0.5.3", + "license": "GPL-3.0-or-later", "dependencies": { - "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.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 - } + "@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" } }, - "@babel/generator": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz", - "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==", + "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, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/helper-annotate-as-pure": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz", - "integrity": "sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og==", + "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==", "dev": true, - "requires": { - "@babel/types": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz", - "integrity": "sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ==", + "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, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-builder-react-jsx": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.7.4.tgz", - "integrity": "sha512-kvbfHJNN9dg4rkEM4xn1s8d1/h6TYNvajy9L1wx4qLn9HFg0IkTsQi4rfBe92nxrPUFcMsHoMV+8rU7MJb3fCA==", + "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, - "requires": { - "@babel/types": "^7.7.4", - "esutils": "^2.0.0" + "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", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@babel/helper-call-delegate": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz", - "integrity": "sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA==", + "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, - "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "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/helper-create-class-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.4.tgz", - "integrity": "sha512-l+OnKACG4uiDHQ/aJT8dwpR+LhCJALxL0mJ6nzjB25e5IPwqV1VOsY7ah6UB1DG+VOXAIMtuC54rFJGiHkxjgA==", + "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==", "dev": true, - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz", - "integrity": "sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A==", + "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==", "dev": true, - "requires": { - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-define-map": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz", - "integrity": "sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg==", + "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==", "dev": true, - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/types": "^7.7.4", - "lodash": "^4.17.13" + "license": "MIT", + "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/helper-explode-assignable-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz", - "integrity": "sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg==", + "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==", "dev": true, - "requires": { - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "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/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==", + "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==", "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "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/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==", + "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==", "dev": true, - "requires": { - "@babel/types": "^7.7.4" + "license": "MIT", + "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/helper-hoist-variables": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz", - "integrity": "sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ==", + "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==", "dev": true, - "requires": { - "@babel/types": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz", - "integrity": "sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw==", + "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==", "dev": true, - "requires": { - "@babel/types": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-module-imports": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz", - "integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==", + "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==", "dev": true, - "requires": { - "@babel/types": "^7.7.4" + "license": "MIT", + "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/helper-module-transforms": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz", - "integrity": "sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw==", + "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==", "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-simple-access": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4", - "lodash": "^4.17.13" + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-optimise-call-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz", - "integrity": "sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg==", + "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, - "requires": { - "@babel/types": "^7.7.4" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@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-regex": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", - "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", + "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, - "requires": { - "lodash": "^4.17.13" + "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/helper-remap-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz", - "integrity": "sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw==", + "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==", "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-wrap-function": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "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/helper-replace-supers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz", - "integrity": "sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg==", + "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==", "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-simple-access": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz", - "integrity": "sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A==", + "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==", "dev": true, - "requires": { - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@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==", + "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, - "requires": { - "@babel/types": "^7.7.4" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-wrap-function": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz", - "integrity": "sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg==", + "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, - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@babel/helpers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", - "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", + "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, - "requires": { - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "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, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.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.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz", - "integrity": "sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4", - "@babel/plugin-syntax-async-generators": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/plugin-proposal-class-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz", - "integrity": "sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw==", + "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==", "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-proposal-dynamic-import": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.4.tgz", - "integrity": "sha512-StH+nGAdO6qDB1l8sZ5UBV8AC3F2VW2I8Vfld73TMKyptMU9DY5YsJAS8U81+vEtxcH3Y/La0wG0btDrhpnhjQ==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/plugin-proposal-function-bind": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.7.4.tgz", - "integrity": "sha512-0qJlxfYKHs/JUg+JFISl29YObUCKAOQ0ENHMYoxErBFp58XTXwQEsrVPhs2TGL3cxI21XPs2fpommO6zmCd3/A==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-function-bind": "^7.7.4" + "license": "MIT", + "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/plugin-proposal-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz", - "integrity": "sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.7.tgz", - "integrity": "sha512-3qp9I8lelgzNedI3hrhkvhaEYree6+WHnyA/q4Dza9z7iEIs1eyhWyJnetk3jJ69RT0AT4G0UhEGwyGFJ7GUuQ==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.7.4" + "license": "MIT", + "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/plugin-proposal-unicode-property-regex": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.7.tgz", - "integrity": "sha512-80PbkKyORBUVm1fbTLrHpYdJxMThzM1UqFGh0ALEhO9TYbG86Ah9zQYAB/84axz2vcxefDLdZwWwZNlYARlu9w==", + "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==", "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-syntax-async-generators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz", - "integrity": "sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g==", + "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.", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-syntax-dynamic-import": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz", - "integrity": "sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-syntax-function-bind": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.7.4.tgz", - "integrity": "sha512-dF3QkkaFA3Z7eiD2Cv7Y5x4w2sAKQVHUV2hLqi9iPKexw+/oqpL4crnnalg/Lq31XN33cH3G41kONSCqu06i/Q==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz", - "integrity": "sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg==", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-jsx": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz", - "integrity": "sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg==", + "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, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@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==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ==", + "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, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz", - "integrity": "sha512-wdsOw0MvkL1UIgiQ/IFr3ETcfv1xb8RMM0H9wbiDyLaJFyiDg5oZvDLCXosIXmFeIlweML5iOBXAkqddkYNizg==", + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz", - "integrity": "sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA==", + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz", - "integrity": "sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg==", + "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, - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz", - "integrity": "sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-block-scoping": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz", - "integrity": "sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg==", + "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, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.13" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-classes": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz", - "integrity": "sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg==", + "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, - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-define-map": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@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" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.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==", + "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-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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.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==", + "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-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==", + "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", "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-computed-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz", - "integrity": "sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-transform-destructuring": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz", - "integrity": "sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.7.tgz", - "integrity": "sha512-b4in+YlTeE/QmTgrllnb3bHA0HntYvjz8O3Mcbx75UBPJA2xhb5A8nle498VhxSXJHQefjtQxpnLPehDJ4TRlg==", + "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==", "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "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" } }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz", - "integrity": "sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz", - "integrity": "sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ==", + "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==", "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-transform-for-of": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz", - "integrity": "sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-transform-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz", - "integrity": "sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g==", + "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==", "dev": true, - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-transform-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz", - "integrity": "sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "license": "MIT", + "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/plugin-transform-member-expression-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz", - "integrity": "sha512-9VMwMO7i69LHTesL0RdGy93JU6a+qOPuvB4F4d0kR0zyVjJRVJRaoaGjhtki6SzQUu8yen/vxPKN6CWnCUw6bA==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "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" } }, - "@babel/plugin-transform-modules-amd": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz", - "integrity": "sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ==", + "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==", "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.7.5", + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "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-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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "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-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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "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" + } + }, + "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==", + "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-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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "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-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==", + "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-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==", + "dev": true, + "license": "MIT", + "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" + } + }, + "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==", + "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", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.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-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.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" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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==", + "dev": true, + "license": "MIT", + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz", - "integrity": "sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q==", + "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==", "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.7.5", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.7.4", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz", - "integrity": "sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz", - "integrity": "sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz", - "integrity": "sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz", - "integrity": "sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz", - "integrity": "sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.7.tgz", - "integrity": "sha512-OhGSrf9ZBrr1fw84oFXj5hgi8Nmg+E2w5L7NhnG0lPvpDtqd7dbyilM2/vR8CKbJ907RyxPh2kj6sBCSSfI9Ew==", - "dev": true, - "requires": { - "@babel/helper-call-delegate": "^7.7.4", - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz", - "integrity": "sha512-MatJhlC4iHsIskWYyawl53KuHrt+kALSADLQQ/HkhTjX954fkxIEh4q5slL4oRAnsm/eDoZ4q0CIZpcqBuxhJQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.7.7.tgz", - "integrity": "sha512-SlPjWPbva2+7/ZJbGcoqjl4LsQaLpKEzxW9hcxU7675s24JmdotJOSJ4cgAbV82W3FcZpHIGmRZIlUL8ayMvjw==", - "dev": true, - "requires": { - "@babel/helper-builder-react-jsx": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.7.4" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz", - "integrity": "sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.0" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.7.4.tgz", - "integrity": "sha512-OrPiUB5s5XvkCO1lS7D8ZtHcswIC57j62acAnJZKqGGnHP+TIc/ljQSrgdX/QyOTdEK5COAhuc820Hi1q2UgLQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.7.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.6.tgz", - "integrity": "sha512-tajQY+YmXR7JjTwRvwL4HePqoL3DYxpYXIHKVvrOIvJmeHe2y1w4tz5qz9ObUDC9m76rCzIMPyn4eERuwA4a4A==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "resolve": "^1.8.1", - "semver": "^5.5.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz", - "integrity": "sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz", - "integrity": "sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz", - "integrity": "sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz", - "integrity": "sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz", - "integrity": "sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz", - "integrity": "sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/preset-env": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.7.tgz", - "integrity": "sha512-pCu0hrSSDVI7kCVUOdcMNQEbOPJ52E+LrQ14sN8uL2ALfSqePZQlKrOy+tM4uhEdYlCHi4imr8Zz2cZe9oSdIg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.7.4", - "@babel/plugin-proposal-dynamic-import": "^7.7.4", - "@babel/plugin-proposal-json-strings": "^7.7.4", - "@babel/plugin-proposal-object-rest-spread": "^7.7.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.7.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.7.7", - "@babel/plugin-syntax-async-generators": "^7.7.4", - "@babel/plugin-syntax-dynamic-import": "^7.7.4", - "@babel/plugin-syntax-json-strings": "^7.7.4", - "@babel/plugin-syntax-object-rest-spread": "^7.7.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.7.4", - "@babel/plugin-syntax-top-level-await": "^7.7.4", - "@babel/plugin-transform-arrow-functions": "^7.7.4", - "@babel/plugin-transform-async-to-generator": "^7.7.4", - "@babel/plugin-transform-block-scoped-functions": "^7.7.4", - "@babel/plugin-transform-block-scoping": "^7.7.4", - "@babel/plugin-transform-classes": "^7.7.4", - "@babel/plugin-transform-computed-properties": "^7.7.4", - "@babel/plugin-transform-destructuring": "^7.7.4", - "@babel/plugin-transform-dotall-regex": "^7.7.7", - "@babel/plugin-transform-duplicate-keys": "^7.7.4", - "@babel/plugin-transform-exponentiation-operator": "^7.7.4", - "@babel/plugin-transform-for-of": "^7.7.4", - "@babel/plugin-transform-function-name": "^7.7.4", - "@babel/plugin-transform-literals": "^7.7.4", - "@babel/plugin-transform-member-expression-literals": "^7.7.4", - "@babel/plugin-transform-modules-amd": "^7.7.5", - "@babel/plugin-transform-modules-commonjs": "^7.7.5", - "@babel/plugin-transform-modules-systemjs": "^7.7.4", - "@babel/plugin-transform-modules-umd": "^7.7.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4", - "@babel/plugin-transform-new-target": "^7.7.4", - "@babel/plugin-transform-object-super": "^7.7.4", - "@babel/plugin-transform-parameters": "^7.7.7", - "@babel/plugin-transform-property-literals": "^7.7.4", - "@babel/plugin-transform-regenerator": "^7.7.5", - "@babel/plugin-transform-reserved-words": "^7.7.4", - "@babel/plugin-transform-shorthand-properties": "^7.7.4", - "@babel/plugin-transform-spread": "^7.7.4", - "@babel/plugin-transform-sticky-regex": "^7.7.4", - "@babel/plugin-transform-template-literals": "^7.7.4", - "@babel/plugin-transform-typeof-symbol": "^7.7.4", - "@babel/plugin-transform-unicode-regex": "^7.7.4", - "@babel/types": "^7.7.4", - "browserslist": "^4.6.0", - "core-js-compat": "^3.6.0", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.5.0" - } - }, - "@babel/register": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.7.7.tgz", - "integrity": "sha512-S2mv9a5dc2pcpg/ConlKZx/6wXaEwHeqfo7x/QbXsdCAZm+WJC1ekVvL1TVxNsedTs5y/gG63MhJTEsmwmjtiA==", - "dev": true, - "requires": { + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", - "lodash": "^4.17.13", "make-dir": "^2.1.0", - "pirates": "^4.0.0", + "pirates": "^4.0.6", "source-map-support": "^0.5.16" - } - }, - "@babel/runtime": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz", - "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "@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, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@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, - "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" }, - "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 - } + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@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==", + "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, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "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" + } + }, + "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==", + "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" + } + }, + "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==", + "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" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", "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" - } - }, - "@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" }, - "dependencies": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } + "engines": { + "node": ">=6.9.0" } }, - "@jest/core": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", - "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "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, - "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", + "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.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" + "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" }, - "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" - } + "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 } } }, - "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "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==", "dev": true, - "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "@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==", + "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, - "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "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" } }, - "@jest/reporters": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", - "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "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, - "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", + "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", "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" + "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" }, - "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 + "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 } } }, - "@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==", + "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==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "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", "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "graceful-fs": "^4.2.9" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", "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 - } - } - }, - "@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, - "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": { - "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": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "collect-v8-coverage": "^1.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 - }, - "@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==", + "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, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "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" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "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", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, - "@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==", + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@babel/types": "^7.0.0" } }, - "@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==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@types/babel__traverse": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.8.tgz", - "integrity": "sha512-yGeB2dHEdvxjP0y4UbRtQaSkXJ9649fYCmIdRoul5kfAoGCwxuCbMhag0k3RPfnuh9kPGm8x89btcfDEXdVWGw==", + "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==", "dev": true, - "requires": { - "@babel/types": "^7.3.0" + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" } }, - "@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 - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "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, - "requires": { - "@types/events": "*", + "license": "MIT" + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/minimatch": "*", "@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 - }, - "@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==", + "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, - "requires": { + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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==", + "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": { "@types/istanbul-lib-coverage": "*" } }, - "@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==", + "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==", "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", + "license": "MIT", + "dependencies": { "@types/istanbul-lib-report": "*" } }, - "@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==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, - "requires": { - "source-map": "^0.6.1" - }, + "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": { - "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 - } + "undici-types": "~6.19.2" } }, - "@types/webpack": { - "version": "4.41.8", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.8.tgz", - "integrity": "sha512-mh4litLHTlDG84TGCFv1pZldndI34vkrW9Mks++Zx4KET7DRMoCXUvLbTISiuF4++fMgNnhV9cc1nCXJQyBYbQ==", + "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, - "requires": { - "@types/anymatch": "*", + "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": { + "source-map": "^0.6.1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/node": "*", - "@types/tapable": "*", + "@types/tapable": "^1", "@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 - } } }, - "@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==", + "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==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@types/node": "*", "@types/source-list-map": "*", - "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 - } + "source-map": "^0.7.3" } }, - "@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==", + "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, - "requires": { + "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", + "dependencies": { "@types/yargs-parser": "*" } }, - "@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 - }, - "@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "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, - "requires": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "@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 - }, - "@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 - }, - "@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 - }, - "@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==", + "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, - "requires": { - "@webassemblyjs/wast-printer": "1.9.0" + "license": "MIT" + }, + "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" + }, + "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" + }, + "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==", + "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" } }, - "@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 - }, - "@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==", + "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, - "requires": { - "@webassemblyjs/ast": "1.9.0" + "license": "MIT" + }, + "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==", + "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" } }, - "@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==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, - "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": { + "license": "MIT", + "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, - "@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, - "requires": { + "license": "Apache-2.0", + "dependencies": { "@xtuc/long": "4.2.2" } }, - "@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 - }, - "@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==", + "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, - "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" + "license": "MIT" + }, + "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==", + "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" } }, - "@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==", + "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==", "dev": true, - "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" + "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" } }, - "@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==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" + "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" } }, - "@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==", + "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==", "dev": true, - "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" + "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" } }, - "@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==", + "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==", "dev": true, - "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", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, - "@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, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@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 - }, - "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, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - }, - "dependencies": { - "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 - } - } - }, - "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, - "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" - } - }, - "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 - }, - "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 - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "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 - }, - "ansi-regex": { + "node_modules/@webpack-cli/configtest": { "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==", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "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, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "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-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "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, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" + "license": "MIT", + "engines": { + "node": ">=14.15.0" }, - "dependencies": { - "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" - } - } + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "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 - }, - "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": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } - } - }, - "babel-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", - "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", - "dev": true, - "requires": { - "find-cache-dir": "^2.1.0", - "loader-utils": "^1.4.0", - "mkdirp": "^0.5.3", - "pify": "^4.0.1", - "schema-utils": "^2.6.5" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "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, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - } - }, - "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, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "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, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.9.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "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 - } - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "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" - } - } - } - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "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": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": 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" - } - }, - "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, - "requires": { - "inherits": "~2.0.0" - } - }, - "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": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "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" - }, - "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" - } - } - } - }, - "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.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "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.8.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz", - "integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001015", - "electron-to-chromium": "^1.3.322", - "node-releases": "^1.1.42" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-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" - } - }, - "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" - } - }, - "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": "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, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001017", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001017.tgz", - "integrity": "sha512-EDnZyOJ6eYh6lHmCvCdHAFbfV4KJ9lSdfv4h/ppEhrU/Yudkl7jujwMZ1we6RX7DXqBfT04pVMQ4J+1wcTlsKA==", - "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, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": 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" - }, - "dependencies": { - "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 - } - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "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, - "requires": { - "@types/webpack": "^4.4.31", - "del": "^4.1.1" - }, - "dependencies": { - "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" - } - } - } - }, - "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, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "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, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "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 - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "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": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "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, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "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, - "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" - }, - "dependencies": { - "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" - } - } - } - }, - "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.6.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.1.tgz", - "integrity": "sha512-186WjSik2iTGfDjfdCZAxv2ormxtKgemjC3SI6PL31qOA0j5LhTDVjHChccoc7brwLvpvLPiMyRlcO88C4l1QQ==", - "dev": true - }, - "core-js-compat": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.1.tgz", - "integrity": "sha512-2Tl1EuxZo94QS2VeH28Ebf5g3xbPZG/hj/N5HDDy4XMP/ImR0JIer/nggQRiMN91Q54JVkGbytf42wO29oXVHg==", - "dev": true, - "requires": { - "browserslist": "^4.8.2", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "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.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "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.4.2", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz", - "integrity": "sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA==", - "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.23", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.1.1", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.0.2", - "schema-utils": "^2.6.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 - }, - "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 - } - } - }, - "css.gg": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/css.gg/-/css.gg-1.0.6.tgz", - "integrity": "sha512-Bv8GTVkeuSqqkgdCJ+tJopRxf/mp/wP6hkL13BdCSs3FadD0GWyU3gKdjuaaFkfxkgYK+GhjSX3EA+cXLHBFpA==" - }, - "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, - "requires": { - "cssom": "0.3.x" - } - }, - "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, - "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": { - "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" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "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": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" - }, - "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, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { + "node_modules/@webpack-cli/info": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "license": "MIT", + "engines": { + "node": ">=14.15.0" }, - "dependencies": { - "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" - } - } + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "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==", + "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, - "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" - } - }, - "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, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "electron-to-chromium": { - "version": "1.3.322", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz", - "integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==", - "dev": true - }, - "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "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 - }, - "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, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=14.15.0" }, - "dependencies": { - "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" - } - } - } - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "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, - "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" + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" }, - "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 - } - } - }, - "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" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", - "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "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, + "peerDependenciesMeta": { + "webpack-dev-server": { "optional": true } } }, - "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==", + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "esprima": { + "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 + } + } + }, + "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==", + "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/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" + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "requires": { - "estraverse": "^4.1.0" + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "estraverse": { + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "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" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-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/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==", + "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/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==", + "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": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-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==", + "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" + } + }, + "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==", + "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": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-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==", + "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" + } + }, + "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": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "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" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.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" + } + ], + "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": { + "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": { + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "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==", + "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", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/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" + } + ], + "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" + }, + "node_modules/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": { + "@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" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.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": { + "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": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "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" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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 + } + } + }, + "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==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/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" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.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==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "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==", + "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" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "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==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "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": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "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, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" } }, - "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 - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "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, - "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" - }, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", "dependencies": { - "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" - } - } + "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" } }, - "exit": { + "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "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": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "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" - } - }, - "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" - } - } - } - }, - "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, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "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" + "@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" }, - "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" - } - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, - "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" - } - } - } + "license": "MIT" }, - "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": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "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 - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", "dev": true, - "requires": { + "license": "MIT" + }, + "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" + }, + "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==", + "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": { "bser": "2.1.1" } }, - "fetch-mock": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-8.3.1.tgz", - "integrity": "sha512-7IEIUvkHO6zOHbDSzkMAvkb2mx3N5xy9BS4RjFnIe8kCUDOomoNKBDKGwhTj5E0uuieo8rg55c6cUKorJuk4rg==", + "node_modules/fetch-mock": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-8.3.2.tgz", + "integrity": "sha512-RUdLbhIBTvECX20I8htNhmLRrCplCiOP62srst8UQsSV0m8taJe31PBsQybL7OIq5fEf6tnqVGvQ62ZnZ4IFfQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "babel-runtime": "^6.26.0", "core-js": "^3.0.0", "glob-to-regexp": "^0.4.0", @@ -3514,6054 +4691,4880 @@ "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 + } } }, - "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.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", - "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "dev": true, - "requires": { + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { "loader-utils": "^2.0.0", - "schema-utils": "^2.6.5" + "schema-utils": "^3.0.0" }, - "dependencies": { - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "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" - } - } + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "node_modules/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, - "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" - }, + "license": "MIT", + "optional": true, + "peer": true, "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" - } - } + "@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" } }, - "find-cache-dir": { + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, - "requires": { + "license": "MIT", + "dependencies": { "commondir": "^1.0.1", "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "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, - "requires": { - "locate-path": "^3.0.0" + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "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, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" } }, - "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": { + "node_modules/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==", + "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, - "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 - } + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "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, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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=", + "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, - "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" + "license": "MIT", + "engines": { + "node": ">=6.9.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" - } - }, - "get-caller-file": { + "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 - }, - "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" + "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": { - "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", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": 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, - "requires": { - "is-extglob": "^2.1.0" - } - } + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-to-regexp": { + "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 - }, - "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" - } + "license": "BSD-2-Clause" }, - "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": { + "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 - }, - "globule": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", - "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.12", - "minimatch": "~3.0.2" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "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==", + "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, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "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" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "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, - "requires": { - "function-bind": "^1.1.1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "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, - "requires": { - "ansi-regex": "^2.0.0" - } + "license": "ISC" }, - "has-flag": { + "node_modules/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=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "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=", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, + "license": "MIT", "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" - } - } + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", - "requires": { + "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" } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "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, - "requires": { - "parse-passwd": "^1.0.0" + "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" } }, - "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==", + "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, - "requires": { - "whatwg-encoding": "^1.0.1" + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "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=", + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "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" } }, - "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==", + "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, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "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.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "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": { + "node_modules/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=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "requires": { - "repeating": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=0.8.19" } }, - "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": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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, - "requires": { + "license": "ISC", + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "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 - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "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" - } - } + "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" } }, - "is-arrayish": { + "node_modules/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": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } + "license": "MIT" }, - "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==", + "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, - "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" - }, + "license": "MIT", "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" + "binary-extensions": "^2.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 - } + "engines": { + "node": ">=8" } }, - "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 + "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" + } }, - "is-extglob": { + "node_modules/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 + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "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 + "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" + } }, - "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": { + "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 - }, - "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" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "MIT", "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-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-path-cwd": { + "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 + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "is-path-in-cwd": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "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" - } - } + "engines": { + "node": ">=6" } }, - "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 + "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" + } }, - "is-plain-object": { + "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, - "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" - }, + "license": "MIT", "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 - } + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "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": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, - "isobject": { + "node_modules/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==", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "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 - } + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "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==", + "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, - "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" - } - } + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" } }, - "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==", + "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, - "requires": { + "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": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "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": { - "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" + "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": { - "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": "", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "@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 } } }, - "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==", + "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, - "requires": { - "@jest/types": "^24.9.0", - "execa": "^1.0.0", - "throat": "^4.0.0" + "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" } }, - "jest-config": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", - "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "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, - "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", + "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", - "expect": "^24.9.0", + "dedent": "^1.0.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" + "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": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true + "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 } } }, - "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==", + "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, - "requires": { - "@jest/types": "^24.9.0" + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", + "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, - "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" + "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" } }, - "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==", + "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, - "requires": { - "@jest/types": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.9.0" + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "jest-runner": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", - "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "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, - "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" + "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" } }, - "jest-runtime": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", - "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "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, - "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", + "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.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" + "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" }, - "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 + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": 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" - } + "ts-node": { + "optional": true } } }, - "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==", + "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, - "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" - }, + "license": "MIT", "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 + "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 } } }, - "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==", + "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, - "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", + "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" - }, - "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==", + "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, - "requires": { - "@jest/types": "^24.9.0", - "camelcase": "^5.3.1", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", + "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": "^24.9.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": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "jest-watcher": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", - "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "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, - "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" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "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, - "requires": { + "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": "^6.1.0" + "supports-color": "^8.0.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" - } - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "js-base64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", - "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==", - "dev": true + "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" + } }, - "js-cookie": { + "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==" + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", + "license": "MIT" }, - "js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "dev": true - }, - "js-tokens": { + "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==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, - "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==", + "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, - "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" - }, + "license": "MIT", "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 - } + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "jsesc": { + "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } }, - "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 + "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" }, - "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": { + "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 - }, - "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.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", "dev": true, - "requires": { - "minimist": "^1.2.0" + "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" } }, - "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": { + "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 + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "kleur": { + "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 + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "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": { + "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 - }, - "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" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "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=", + "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, - "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" - }, + "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": { - "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" + "json5": "^2.1.2" }, - "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" - } - } + "engines": { + "node": ">=8.9.0" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "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, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, - "lodash.isequal": { + "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": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT" }, - "lodash.isplainobject": { + "node_modules/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 + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" }, - "lodash.sortby": { + "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" }, - "loose-envify": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "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, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" } }, - "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": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" } }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "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, - "requires": { - "tmpl": "1.0.x" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "requires": { - "p-defer": "^1.0.0" + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" } }, - "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" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "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": { + "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 - }, - "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" + "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" } }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "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, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "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==", + "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, - "requires": { - "mime-db": "1.42.0" + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "mimic-fn": { + "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 - }, - "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" - } - } + "license": "MIT", + "engines": { + "node": ">=6" } }, - "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==", + "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, - "requires": { + "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" - } - }, - "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" }, - "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" - } - } + "engines": { + "node": "*" } }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "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" }, - "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" - } - } + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "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" - }, - "dependencies": { - "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" - } - } - } - }, - "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": { + "node_modules/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.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "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==", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "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" - }, + "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": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true } } }, - "node-int64": { + "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": "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==", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "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" - }, + "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": { - "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, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "events": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", - "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", - "dev": true - }, - "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" - } - }, - "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "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 - }, - "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, - "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - } - } - }, - "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.44", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.44.tgz", - "integrity": "sha512-NwbdvJyR7nrcGrXvKAvzc5raj/NkoJudkarh2yIpJ4t0NH4aqjUDz/486P+ynIW5eokKOfzGNRdYoLfBlomruw==", - "dev": true, - "requires": { - "semver": "^6.3.0" + "path-key": "^3.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 - } + "engines": { + "node": ">=8" } }, - "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": { + "node_modules/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" - } - } + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "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": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "wrappy": "1" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "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, - "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" + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "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=", + "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, - "requires": { - "p-reduce": "^1.0.0" + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "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-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "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==", + "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, - "requires": { + "license": "MIT", + "dependencies": { "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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, - "requires": { - "p-limit": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "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": { + "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 - }, - "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "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" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "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, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "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" } }, - "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": { + "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true + "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" + } }, - "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 - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { + "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "path-is-inside": { + "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": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true, + "license": "(WTFPL OR MIT)" }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "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" + } }, - "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 + "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" }, - "path-to-regexp": { + "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 - }, - "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, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "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" }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "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" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { + "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 + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "pinkie": { + "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "pinkie-promise": { + "node_modules/pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "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, - "requires": { - "node-modules-regexp": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">= 6" } }, - "pkg-dir": { + "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, - "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.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, + "license": "MIT", "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 - }, - "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" - } - } + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "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.2", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", - "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", - "dev": true, - "requires": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.16", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.0" - } - }, - "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, - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - } - }, - "postcss-modules-values": { + "node_modules/pkg-dir/node_modules/find-up": { "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==", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, - "requires": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "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" + } + ], + "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==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, - "postcss-value-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", - "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", - "dev": true + "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" }, - "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": { + "node_modules/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 - }, - "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, - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" }, - "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 - } + "engines": { + "node": ">=4" } }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "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==", + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.3" + "license": "MIT", + "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" } }, - "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": { + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, - "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/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" }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "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==", + "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, - "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" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" }, - "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" - } - }, - "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": { + "node_modules/querystring": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true + "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" + } }, - "randombytes": { + "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "safe-buffer": "^5.1.0" } }, - "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.12.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", - "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", - "dev": true, - "requires": { + "node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "license": "MIT", + "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "react-dom": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", - "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", + "node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.18.0" + "scheduler": "^0.19.1" + }, + "peerDependencies": { + "react": "^16.14.0" } }, - "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-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-redux": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.3.tgz", - "integrity": "sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w==", - "requires": { - "@babel/runtime": "^7.5.5", - "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4", + "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", + "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", - "react-is": "^16.9.0" - } - }, - "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, - "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" + "react-is": "^17.0.2" }, - "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" - } + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true }, - "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" - } + "react-native": { + "optional": true } } }, - "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==", + "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==", "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" + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" } }, - "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, - "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" - } - }, - "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": { + "node_modules/redux-logger": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", - "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=", - "requires": { + "integrity": "sha512-JoCIok7bg/XpqA1JqCqXFypuqBbQzGQySrhFzewB7ThcnysTO30l4VCst86AuB9T9tuT03MAA56Jw2PNhRSNCg==", + "license": "MIT", + "dependencies": { "deep-diff": "^0.3.5" } }, - "redux-mock-store": { + "node_modules/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, - "requires": { + "license": "MIT", + "dependencies": { "lodash.isplainobject": "^4.0.6" } }, - "redux-thunk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", - "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" + "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" } }, - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" }, - "regenerator-transform": { + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", - "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, - "requires": { - "private": "^0.1.6" + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.4" } }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "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==", "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "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" } }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", - "dev": true - }, - "regjsparser": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz", - "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==", - "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "jsesc": "~0.5.0" }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } + "bin": { + "regjsparser": "bin/parser" } }, - "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=", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true, - "requires": { - "is-finite": "^1.0.0" + "bin": { + "jsesc": "bin/jsesc" } }, - "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": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "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==", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "requires": { - "path-parse": "^1.0.6" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "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==", "dev": true, - "requires": { - "resolve-from": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "license": "MIT", + "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" } }, - "resolve-from": { + "node_modules/resolve-cwd": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true + "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" + } }, - "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-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" + } }, - "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/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" + } }, - "rimraf": { + "node_modules/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, - "requires": { + "license": "ISC", + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" } }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } + "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" }, - "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=", + "node_modules/sass": { + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, - "requires": { - "aproba": "^1.1.1" + "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" } }, - "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": { + "node_modules/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, - "requires": { + "license": "MIT", + "dependencies": { "clone-deep": "^4.0.1", "loader-utils": "^1.2.3", "neo-async": "^2.6.1", "schema-utils": "^2.6.1", "semver": "^6.3.0" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "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 } } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "scheduler": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", - "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", + "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, - "requires": { + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "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": { + "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": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, - "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==", + "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==", "dev": true, - "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - }, + "license": "MIT", "dependencies": { - "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 - } - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.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" - } - } - } - }, - "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": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true - }, - "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" + "engines": { + "node": ">= 8.9.0" }, - "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" - } - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "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==", + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "shallow-clone": { + "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==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" } }, - "shebang-command": { + "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": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true, - "requires": { - "shebang-regex": "^1.0.0" + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "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==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "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" - }, + "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" - } - }, - "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": { - "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 - } + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "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==", + "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, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "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==", + "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, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "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==", + "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==", "dev": true, - "requires": { - "extend-shallow": "^3.0.0" + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "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": { + "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "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" - }, + "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" - } - } + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "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, - "requires": { - "readable-stream": "^2.0.1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "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-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "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": { + "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" + "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" }, - "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" - } - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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": { + "node_modules/style-loader": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", "dev": true, - "requires": { - "is-utf8": "^0.2.0" + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "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=", + "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, - "requires": { - "get-stdin": "^4.0.1" + "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" } }, - "style-loader": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz", - "integrity": "sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw==", - "dev": true, - "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.6.4" - } - }, - "supports-color": { + "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "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==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "terser": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.7.tgz", - "integrity": "sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "requires": { + "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", "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "source-map-support": "~0.5.20" }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "license": "MIT", "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 + "@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 } } }, - "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "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==", "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - }, + "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", "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" - } - }, - "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 - } - } - }, - "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, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, - "dependencies": { - "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 - } + "engines": { + "node": ">= 10.13.0" } }, - "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==", + "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==", "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "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" } }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } }, - "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 + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } }, - "to-fast-properties": { + "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" + }, + "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "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=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", "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-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "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": { + "node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, - "requires": { - "punycode": "^2.1.0" - }, + "license": "MIT", "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 - } + "punycode": "^2.1.0" } }, - "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==", + "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, - "requires": { - "glob": "^7.1.2" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "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, - "requires": { - "safe-buffer": "^5.0.1" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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=", + "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, - "requires": { - "prelude-ls": "~1.1.2" - } + "license": "MIT" }, - "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==", + "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, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "unicode-match-property-value-ecmascript": { + "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/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", - "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==", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "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" - } - } - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, - "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 + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", - "dev": 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==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "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=", + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, + "license": "MIT", "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.0", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.0.tgz", - "integrity": "sha512-IzgAAIC8wRrg6NYkFIJY09vtktQcsvU8V6HhtQj9PTefbYImzLB1hufqo4m+RyM5N3mLx5BqJKccgxJS+W3kqw==", - "dev": true, - "requires": { "loader-utils": "^2.0.0", - "mime-types": "^2.1.26", - "schema-utils": "^2.6.5" + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" }, - "dependencies": { - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "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.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", - "dev": true - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dev": true, - "requires": { - "mime-db": "1.44.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 } } }, - "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 + "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==", + "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" + } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "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==", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "license": "MIT" + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, - "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.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", - "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==", + "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, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" } }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.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.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "webidl-conversions": { + "node_modules/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 - }, - "webpack": { - "version": "4.42.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", - "integrity": "sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg==", "dev": true, - "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.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "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", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.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.6.0", - "webpack-sources": "^1.4.1" + "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" }, - "dependencies": { - "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true - }, - "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" - } + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true } } }, - "webpack-cli": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", - "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==", + "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==", "dev": true, - "requires": { - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "enhanced-resolve": "4.1.0", - "findup-sync": "3.0.0", - "global-modules": "2.0.0", - "import-local": "2.0.0", - "interpret": "1.2.0", - "loader-utils": "1.2.3", - "supports-color": "6.1.0", - "v8-compile-cache": "2.0.3", - "yargs": "13.2.4" - }, + "license": "MIT", "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 + "@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 }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "webpack-bundle-analyzer": { + "optional": 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" - } - }, - "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" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.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 - }, - "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" - } - }, - "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" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "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" - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.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 - }, - "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" - } - }, - "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" - } - }, - "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.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "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.0" - } - }, - "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" - } + "webpack-dev-server": { + "optional": true } } }, - "webpack-merge": { + "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": { "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, - "requires": { + "license": "MIT", + "dependencies": { "lodash": "^4.17.15" } }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "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==", "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, + "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", "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/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" } }, - "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": { + "node_modules/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, - "requires": { + "license": "MIT", + "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { - "isexe": "^2.0.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 - }, - "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, - "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" - }, + "license": "ISC", "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" - } - } + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "wrappy": { + "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" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "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==", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } + "license": "ISC" }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "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 - }, - "xtend": { + "node_modules/write-file-atomic": { "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==", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, + "license": "ISC", "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" - } - } + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/package.json b/package.json index 1fec809..d12a88a 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,75 @@ { "name": "newsreader", - "version": "0.1.0", + "version": "0.5.3", "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": "[git@git.fudiggity.nl:5000]:sonny/newsreader.git" + "url": "forgejo.fudiggity.nl:sonny/newsreader" }, "author": "Sonny", "license": "GPL-3.0-or-later", "dependencies": { - "css.gg": "^1.0.6", + "@fortawesome/fontawesome-free": "^5.15.2", "js-cookie": "^2.2.1", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "object-assign": "^4.1.1", - "react-redux": "^7.1.3", + "react-redux": "^7.2.2", "redux": "^4.0.5", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0" }, "devDependencies": { - "@babel/core": "^7.7.7", - "@babel/plugin-proposal-class-properties": "^7.7.4", - "@babel/plugin-proposal-function-bind": "^7.7.4", - "@babel/plugin-syntax-dynamic-import": "^7.7.4", - "@babel/plugin-syntax-function-bind": "^7.7.4", - "@babel/plugin-transform-react-jsx": "^7.7.7", - "@babel/plugin-transform-runtime": "^7.7.6", - "@babel/preset-env": "^7.7.7", - "@babel/register": "^7.7.7", - "@babel/runtime": "^7.7.7", - "babel-jest": "^24.9.0", - "babel-loader": "^8.1.0", + "@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": "^3.4.2", - "fetch-mock": "^8.3.1", - "file-loader": "^6.0.0", - "jest": "^24.9.0", - "mini-css-extract-plugin": "^0.9.0", - "node-fetch": "^2.6.0", - "node-sass": "^4.14.1", + "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.12.0", - "react-dom": "^16.12.0", + "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": "^1.1.3", - "url-loader": "^4.1.0", - "webpack": "^4.42.1", - "webpack-cli": "^3.3.11", + "style-loader": "^2.0.0", + "url-loader": "^4.1.1", + "webpack": "^5.94.0", + "webpack-cli": "^5.1.4", "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 deleted file mode 100644 index cab45d1..0000000 --- a/poetry.lock +++ /dev/null @@ -1,1189 +0,0 @@ -[[package]] -category = "main" -description = "Low-level AMQP client for Python (fork of amqplib)." -name = "amqp" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.5.2" - -[package.dependencies] -vine = ">=1.1.3,<5.0.0a1" - -[[package]] -category = "dev" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -name = "appdirs" -optional = false -python-versions = "*" -version = "1.4.3" - -[[package]] -category = "main" -description = "ASGI specs, helper code, and adapters" -name = "asgiref" -optional = false -python-versions = ">=3.5" -version = "3.2.7" - -[package.extras] -tests = ["pytest (>=4.3.0,<4.4.0)", "pytest-asyncio (>=0.10.0,<0.11.0)"] - -[[package]] -category = "dev" -description = "Classes Without Boilerplate" -name = "attrs" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.3.0" - -[package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] - -[[package]] -category = "dev" -description = "Removes unused imports and unused variables" -name = "autoflake" -optional = false -python-versions = "*" -version = "1.3.1" - -[package.dependencies] -pyflakes = ">=1.1.0" - -[[package]] -category = "main" -description = "Screen-scraping library" -name = "beautifulsoup4" -optional = false -python-versions = "*" -version = "4.9.0" - -[package.dependencies] -soupsieve = [">1.2", "<2.0"] - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -category = "main" -description = "Python multiprocessing fork with improvements and bugfixes" -name = "billiard" -optional = false -python-versions = "*" -version = "3.6.3.0" - -[[package]] -category = "dev" -description = "The uncompromising code formatter." -name = "black" -optional = false -python-versions = ">=3.6" -version = "19.3b0" - -[package.dependencies] -appdirs = "*" -attrs = ">=18.1.0" -click = ">=6.5" -toml = ">=0.9.4" - -[package.extras] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] - -[[package]] -category = "main" -description = "An easy safelist-based HTML-sanitizing tool." -name = "bleach" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "3.1.4" - -[package.dependencies] -six = ">=1.9.0" -webencodings = "*" - -[[package]] -category = "main" -description = "Distributed Task Queue." -name = "celery" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*," -version = "4.4.2" - -[package.dependencies] -billiard = ">=3.6.3.0,<4.0" -kombu = ">=4.6.8,<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"] -consul = ["python-consul"] -cosmosdbsql = ["pydocumentdb (2.3.2)"] -couchbase = ["couchbase", "couchbase-cffi"] -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 (>=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.2)"] -tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=1.3.1)"] -zstd = ["zstandard"] - -[[package]] -category = "main" -description = "Python package for providing Mozilla's CA Bundle." -name = "certifi" -optional = false -python-versions = "*" -version = "2020.4.5.1" - -[[package]] -category = "main" -description = "Universal encoding detector for Python 2 and 3" -name = "chardet" -optional = false -python-versions = "*" -version = "3.0.4" - -[[package]] -category = "dev" -description = "Composable command line interface toolkit" -name = "click" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "7.1.1" - -[[package]] -category = "main" -description = "Python client library for Core API." -name = "coreapi" -optional = false -python-versions = "*" -version = "2.3.3" - -[package.dependencies] -coreschema = "*" -itypes = "*" -requests = "*" -uritemplate = "*" - -[[package]] -category = "main" -description = "Core Schema." -name = "coreschema" -optional = false -python-versions = "*" -version = "0.0.4" - -[package.dependencies] -jinja2 = "*" - -[[package]] -category = "dev" -description = "Code coverage measurement for Python" -name = "coverage" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "5.1" - -[package.extras] -toml = ["toml"] - -[[package]] -category = "main" -description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." -name = "django" -optional = false -python-versions = ">=3.6" -version = "3.0.7" - -[package.dependencies] -asgiref = ">=3.2,<4.0" -pytz = "*" -sqlparse = ">=0.2.2" - -[package.extras] -argon2 = ["argon2-cffi (>=16.1.0)"] -bcrypt = ["bcrypt"] - -[[package]] -category = "main" -description = "A helper class for handling configuration defaults of packaged apps gracefully." -name = "django-appconf" -optional = false -python-versions = "*" -version = "1.0.4" - -[package.dependencies] -django = "*" - -[[package]] -category = "main" -description = "Keep track of failed login attempts in Django-powered sites." -name = "django-axes" -optional = false -python-versions = "~=3.6" -version = "5.3.1" - -[package.dependencies] -django = ">=1.11" -django-appconf = ">=1.0.3" -django-ipware = ">=2.0.2" - -[[package]] -category = "main" -description = "Database-backed Periodic Tasks." -name = "django-celery-beat" -optional = false -python-versions = "*" -version = "2.0.0" - -[package.dependencies] -Django = ">=1.11.17" -celery = "*" -django-timezone-field = ">=4.0,<5.0" -python-crontab = ">=2.3.4" - -[[package]] -category = "dev" -description = "A configurable set of panels that display various debug information about the current request/response." -name = "django-debug-toolbar" -optional = false -python-versions = ">=3.5" -version = "2.2" - -[package.dependencies] -Django = ">=1.11" -sqlparse = ">=0.2.0" - -[[package]] -category = "dev" -description = "Extensions for Django" -name = "django-extensions" -optional = false -python-versions = "*" -version = "2.2.9" - -[package.dependencies] -six = ">=1.2" - -[[package]] -category = "main" -description = "A Django utility application that returns client's real IP address" -name = "django-ipware" -optional = false -python-versions = "*" -version = "2.1.0" - -[[package]] -category = "main" -description = "An extensible user-registration application for Django" -name = "django-registration-redux" -optional = false -python-versions = "*" -version = "2.7" - -[[package]] -category = "main" -description = "A Django app providing database and form fields for pytz timezone objects." -name = "django-timezone-field" -optional = false -python-versions = ">=3.5" -version = "4.0" - -[package.dependencies] -django = ">=2.2" -pytz = "*" - -[[package]] -category = "main" -description = "Web APIs for Django, made easy." -name = "djangorestframework" -optional = false -python-versions = ">=3.5" -version = "3.11.0" - -[package.dependencies] -django = ">=1.11" - -[[package]] -category = "main" -description = "Automated generation of real Swagger/OpenAPI 2.0 schemas from Django Rest Framework code." -name = "drf-yasg" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "1.17.1" - -[package.dependencies] -Django = ">=1.11.7" -coreapi = ">=2.3.3" -coreschema = ">=0.0.4" -djangorestframework = ">=3.8" -inflection = ">=0.3.1" -packaging = "*" -"ruamel.yaml" = ">=0.15.34" -six = ">=1.10.0" -uritemplate = ">=3.0.0" - -[package.extras] -validation = ["swagger-spec-validator (>=2.1.0)"] - -[[package]] -category = "dev" -description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." -name = "factory-boy" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.12.0" - -[package.dependencies] -Faker = ">=0.7.0" - -[[package]] -category = "dev" -description = "Faker is a Python package that generates fake data for you." -name = "faker" -optional = false -python-versions = ">=3.4" -version = "4.0.2" - -[package.dependencies] -python-dateutil = ">=2.4" -text-unidecode = "1.3" - -[[package]] -category = "main" -description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds" -name = "feedparser" -optional = false -python-versions = "*" -version = "5.2.1" - -[[package]] -category = "dev" -description = "Let your Python tests travel through time" -name = "freezegun" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.3.15" - -[package.dependencies] -python-dateutil = ">=1.0,<2.0 || >2.0" -six = "*" - -[[package]] -category = "main" -description = "WSGI HTTP Server for UNIX" -name = "gunicorn" -optional = false -python-versions = ">=3.4" -version = "20.0.4" - -[package.dependencies] -setuptools = ">=3.0" - -[package.extras] -eventlet = ["eventlet (>=0.9.7)"] -gevent = ["gevent (>=0.13)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - -[[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" -name = "idna" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.9" - -[[package]] -category = "main" -description = "Read metadata from Python packages" -marker = "python_version < \"3.8\"" -name = "importlib-metadata" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.6.0" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "importlib-resources"] - -[[package]] -category = "main" -description = "A port of Ruby on Rails inflector to Python" -name = "inflection" -optional = false -python-versions = ">=3.5" -version = "0.4.0" - -[[package]] -category = "dev" -description = "A Python utility / library to sort Python imports." -name = "isort" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "4.3.21" - -[package.extras] -pipfile = ["pipreqs", "requirementslib"] -pyproject = ["toml"] -requirements = ["pipreqs", "pip-api"] -xdg_home = ["appdirs (>=1.4.0)"] - -[[package]] -category = "main" -description = "Simple immutable types for python." -name = "itypes" -optional = false -python-versions = "*" -version = "1.1.0" - -[[package]] -category = "main" -description = "A very fast and expressive template engine." -name = "jinja2" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.11.1" - -[package.dependencies] -MarkupSafe = ">=0.23" - -[package.extras] -i18n = ["Babel (>=0.8)"] - -[[package]] -category = "main" -description = "Messaging library for Python." -name = "kombu" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "4.6.8" - -[package.dependencies] -amqp = ">=2.5.2,<2.6" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.18" - -[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]] -category = "main" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -name = "lxml" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -version = "4.5.0" - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["beautifulsoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -category = "main" -description = "Safely add untrusted strings to HTML/XML markup." -name = "markupsafe" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.1.1" - -[[package]] -category = "main" -description = "Core utilities for Python packages" -name = "packaging" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.3" - -[package.dependencies] -pyparsing = ">=2.0.2" -six = "*" - -[[package]] -category = "main" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -name = "psycopg2-binary" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "2.8.5" - -[[package]] -category = "dev" -description = "passive checker of Python programs" -name = "pyflakes" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.2.0" - -[[package]] -category = "main" -description = "Python parsing module" -name = "pyparsing" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.7" - -[[package]] -category = "main" -description = "Python Crontab API" -name = "python-crontab" -optional = false -python-versions = "*" -version = "2.4.1" - -[package.dependencies] -python-dateutil = "*" - -[package.extras] -cron-description = ["cron-descriptor"] -cron-schedule = ["croniter"] - -[[package]] -category = "main" -description = "Extensions to the standard Python datetime module" -name = "python-dateutil" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -version = "2.8.1" - -[package.dependencies] -six = ">=1.5" - -[[package]] -category = "main" -description = "Add .env support to your django/flask apps in development and deployments" -name = "python-dotenv" -optional = false -python-versions = "*" -version = "0.12.0" - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -category = "main" -description = "Pure python memcached client" -name = "python-memcached" -optional = false -python-versions = "*" -version = "1.59" - -[package.dependencies] -six = ">=1.4.0" - -[[package]] -category = "main" -description = "World timezone definitions, modern and historical" -name = "pytz" -optional = false -python-versions = "*" -version = "2019.3" - -[[package]] -category = "main" -description = "Python HTTP for Humans." -name = "requests" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.23.0" - -[package.dependencies] -certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" -idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" - -[package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] - -[[package]] -category = "main" -description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -name = "ruamel.yaml" -optional = false -python-versions = "*" -version = "0.16.10" - -[package.dependencies] -[package.dependencies."ruamel.yaml.clib"] -python = "<3.9" -version = ">=0.1.2" - -[package.extras] -docs = ["ryd"] -jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] - -[[package]] -category = "main" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -marker = "platform_python_implementation == \"CPython\" and python_version < \"3.9\"" -name = "ruamel.yaml.clib" -optional = false -python-versions = "*" -version = "0.2.0" - -[[package]] -category = "main" -description = "Python client for Sentry (https://getsentry.com)" -name = "sentry-sdk" -optional = false -python-versions = "*" -version = "0.15.1" - -[package.dependencies] -certifi = "*" -urllib3 = ">=1.10.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -flask = ["flask (>=0.11)", "blinker (>=1.1)"] -pyspark = ["pyspark (>=2.4.4)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -tornado = ["tornado (>=5)"] - -[[package]] -category = "main" -description = "Python 2 and 3 compatibility utilities" -name = "six" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.14.0" - -[[package]] -category = "main" -description = "A modern CSS selector implementation for Beautiful Soup." -name = "soupsieve" -optional = false -python-versions = "*" -version = "1.9.5" - -[[package]] -category = "main" -description = "Non-validating SQL parser" -name = "sqlparse" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.3.1" - -[[package]] -category = "dev" -description = "Traceback serialization library." -name = "tblib" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "1.6.0" - -[[package]] -category = "dev" -description = "The most basic Text::Unidecode port" -name = "text-unidecode" -optional = false -python-versions = "*" -version = "1.3" - -[[package]] -category = "dev" -description = "Python Library for Tom's Obvious, Minimal Language" -name = "toml" -optional = false -python-versions = "*" -version = "0.10.0" - -[[package]] -category = "main" -description = "URI templates" -name = "uritemplate" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.0.1" - -[[package]] -category = "main" -description = "HTTP library with thread-safe connection pooling, file post, and more." -name = "urllib3" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.8" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] - -[[package]] -category = "main" -description = "Promises, promises, promises." -name = "vine" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.3.0" - -[[package]] -category = "main" -description = "Character encoding aliases for legacy web content" -name = "webencodings" -optional = false -python-versions = "*" -version = "0.5.1" - -[[package]] -category = "main" -description = "Backport of pathlib-compatible object wrapper for zip files" -marker = "python_version < \"3.8\"" -name = "zipp" -optional = false -python-versions = ">=3.6" -version = "3.1.0" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] - -[metadata] -content-hash = "6b207d452b10de2399c4c49118da997dda6ed1bb0437963c3f415ecd3d806fe5" -python-versions = "^3.7" - -[metadata.files] -amqp = [ - {file = "amqp-2.5.2-py2.py3-none-any.whl", hash = "sha256:6e649ca13a7df3faacdc8bbb280aa9a6602d22fd9d545336077e573a1f4ff3b8"}, - {file = "amqp-2.5.2.tar.gz", hash = "sha256:77f1aef9410698d20eaeac5b73a87817365f457a507d82edf292e12cbb83b08d"}, -] -appdirs = [ - {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, - {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, -] -asgiref = [ - {file = "asgiref-3.2.7-py2.py3-none-any.whl", hash = "sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c"}, - {file = "asgiref-3.2.7.tar.gz", hash = "sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5"}, -] -attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, -] -autoflake = [ - {file = "autoflake-1.3.1.tar.gz", hash = "sha256:680cb9dade101ed647488238ccb8b8bfb4369b53d58ba2c8cdf7d5d54e01f95b"}, -] -beautifulsoup4 = [ - {file = "beautifulsoup4-4.9.0-py2-none-any.whl", hash = "sha256:a4bbe77fd30670455c5296242967a123ec28c37e9702a8a81bd2f20a4baf0368"}, - {file = "beautifulsoup4-4.9.0-py3-none-any.whl", hash = "sha256:d4e96ac9b0c3a6d3f0caae2e4124e6055c5dcafde8e2f831ff194c104f0775a0"}, - {file = "beautifulsoup4-4.9.0.tar.gz", hash = "sha256:594ca51a10d2b3443cbac41214e12dbb2a1cd57e1a7344659849e2e20ba6a8d8"}, -] -billiard = [ - {file = "billiard-3.6.3.0-py3-none-any.whl", hash = "sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede"}, - {file = "billiard-3.6.3.0.tar.gz", hash = "sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a"}, -] -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.1.4-py2.py3-none-any.whl", hash = "sha256:cc8da25076a1fe56c3ac63671e2194458e0c4d9c7becfd52ca251650d517903c"}, - {file = "bleach-3.1.4.tar.gz", hash = "sha256:e78e426105ac07026ba098f04de8abe9b6e3e98b5befbf89b51a5ef0a4292b03"}, -] -celery = [ - {file = "celery-4.4.2-py2.py3-none-any.whl", hash = "sha256:5b4b37e276033fe47575107a2775469f0b721646a08c96ec2c61531e4fe45f2a"}, - {file = "celery-4.4.2.tar.gz", hash = "sha256:108a0bf9018a871620936c33a3ee9f6336a89f8ef0a0f567a9001f4aa361415f"}, -] -certifi = [ - {file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"}, - {file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"}, -] -chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, -] -click = [ - {file = "click-7.1.1-py2.py3-none-any.whl", hash = "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"}, - {file = "click-7.1.1.tar.gz", hash = "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc"}, -] -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.1-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65"}, - {file = "coverage-5.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2"}, - {file = "coverage-5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04"}, - {file = "coverage-5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6"}, - {file = "coverage-5.1-cp27-cp27m-win32.whl", hash = "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796"}, - {file = "coverage-5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730"}, - {file = "coverage-5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0"}, - {file = "coverage-5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a"}, - {file = "coverage-5.1-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf"}, - {file = "coverage-5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9"}, - {file = "coverage-5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768"}, - {file = "coverage-5.1-cp35-cp35m-win32.whl", hash = "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2"}, - {file = "coverage-5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7"}, - {file = "coverage-5.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0"}, - {file = "coverage-5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019"}, - {file = "coverage-5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c"}, - {file = "coverage-5.1-cp36-cp36m-win32.whl", hash = "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1"}, - {file = "coverage-5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7"}, - {file = "coverage-5.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355"}, - {file = "coverage-5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489"}, - {file = "coverage-5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd"}, - {file = "coverage-5.1-cp37-cp37m-win32.whl", hash = "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e"}, - {file = "coverage-5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a"}, - {file = "coverage-5.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55"}, - {file = "coverage-5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c"}, - {file = "coverage-5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef"}, - {file = "coverage-5.1-cp38-cp38-win32.whl", hash = "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24"}, - {file = "coverage-5.1-cp38-cp38-win_amd64.whl", hash = "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0"}, - {file = "coverage-5.1-cp39-cp39-win32.whl", hash = "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4"}, - {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"}, - {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"}, -] -django = [ - {file = "Django-3.0.7-py3-none-any.whl", hash = "sha256:e1630333248c9b3d4e38f02093a26f1e07b271ca896d73097457996e0fae12e8"}, - {file = "Django-3.0.7.tar.gz", hash = "sha256:5052b34b34b3425233c682e0e11d658fd6efd587d11335a0203d827224ada8f2"}, -] -django-appconf = [ - {file = "django-appconf-1.0.4.tar.gz", hash = "sha256:be58deb54a43d77d2e1621fe59f787681376d3cd0b8bd8e4758ef6c3a6453380"}, - {file = "django_appconf-1.0.4-py2.py3-none-any.whl", hash = "sha256:1b1d0e1069c843ebe8ae5aa48ec52403b1440402b320c3e3a206a0907e97bb06"}, -] -django-axes = [ - {file = "django-axes-5.3.1.tar.gz", hash = "sha256:23eee8297dfcb5aa780e4925f58d723387afe8ecc8fd6a7e9522d26c95c7b880"}, - {file = "django_axes-5.3.1-py3-none-any.whl", hash = "sha256:49fa9736cbbf7d83a61ed57f7b2ebd65f8d3064bb0c45b945bfa7421288031a1"}, -] -django-celery-beat = [ - {file = "django-celery-beat-2.0.0.tar.gz", hash = "sha256:fdf1255eecfbeb770c6521fe3e69989dfc6373cd5a7f0fe62038d37f80f47e48"}, - {file = "django_celery_beat-2.0.0-py2.py3-none-any.whl", hash = "sha256:fe0b2a1b31d4a6234fea4b31986ddfd4644a48fab216ce1843f3ed0ddd2e9097"}, -] -django-debug-toolbar = [ - {file = "django-debug-toolbar-2.2.tar.gz", hash = "sha256:eabbefe89881bbe4ca7c980ff102e3c35c8e8ad6eb725041f538988f2f39a943"}, - {file = "django_debug_toolbar-2.2-py3-none-any.whl", hash = "sha256:ff94725e7aae74b133d0599b9bf89bd4eb8f5d2c964106e61d11750228c8774c"}, -] -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-2.1.0.tar.gz", hash = "sha256:a7c7a8fd019dbdc9c357e6e582f65034e897572fc79a7e467674efa8aef9d00b"}, -] -django-registration-redux = [ - {file = "django-registration-redux-2.7.tar.gz", hash = "sha256:1aaf08c9c16b7f185ffa36e7251c7a6149fe953f5af21c4f1e01cbe03902520b"}, - {file = "django_registration_redux-2.7-py2.py3-none-any.whl", hash = "sha256:5998a8dbee2a84d66cd56a61c4fb6b1a5be801b083fb1ef53ba04939d8a44606"}, -] -django-timezone-field = [ - {file = "django-timezone-field-4.0.tar.gz", hash = "sha256:7e3620fe2211c2d372fad54db8f86ff884098d018d56fda4dca5e64929e05ffc"}, - {file = "django_timezone_field-4.0-py3-none-any.whl", hash = "sha256:758b7d41084e9ea2e89e59eb616e9b6326e6fbbf9d14b6ef062d624fe8cc6246"}, -] -djangorestframework = [ - {file = "djangorestframework-3.11.0-py3-none-any.whl", hash = "sha256:05809fc66e1c997fd9a32ea5730d9f4ba28b109b9da71fccfa5ff241201fd0a4"}, - {file = "djangorestframework-3.11.0.tar.gz", hash = "sha256:e782087823c47a26826ee5b6fa0c542968219263fb3976ec3c31edab23a4001f"}, -] -drf-yasg = [ - {file = "drf-yasg-1.17.1.tar.gz", hash = "sha256:5572e9d5baab9f6b49318169df9789f7399d0e3c7bdac8fdb8dfccf1d5d2b1ca"}, - {file = "drf_yasg-1.17.1-py2.py3-none-any.whl", hash = "sha256:7d7af27ad16e18507e9392b2afd6b218fbffc432ec8dbea053099a2241e184ff"}, -] -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-4.0.2-py3-none-any.whl", hash = "sha256:b89aa33837498498e15c709eb40c31386408a901a53c7a5e12a425737a767976"}, - {file = "Faker-4.0.2.tar.gz", hash = "sha256:2d3f866ef25e1a5af80e7b0ceeacc3c92dec5d0fdbad3e2cb6adf6e60b22188f"}, -] -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"}, -] -gunicorn = [ - {file = "gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"}, - {file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"}, -] -idna = [ - {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, - {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, -] -importlib-metadata = [ - {file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"}, - {file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"}, -] -inflection = [ - {file = "inflection-0.4.0-py2.py3-none-any.whl", hash = "sha256:9a15d3598f01220e93f2207c432cfede50daff53137ce660fb8be838ef1ca6cc"}, - {file = "inflection-0.4.0.tar.gz", hash = "sha256:32a5c3341d9583ec319548b9015b7fbdf8c429cbcb575d326c33ae3a0e90d52c"}, -] -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.1.0.tar.gz", hash = "sha256:c6e77bb9fd68a4bfeb9d958fea421802282451a25bac4913ec94db82a899c073"}, -] -jinja2 = [ - {file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"}, - {file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"}, -] -kombu = [ - {file = "kombu-4.6.8-py2.py3-none-any.whl", hash = "sha256:598e7e749d6ab54f646b74b2d2df67755dee13894f73ab02a2a9feb8870c7cb2"}, - {file = "kombu-4.6.8.tar.gz", hash = "sha256:2d1cda774126a044d91a7ff5fa6d09edf99f46924ab332a810760fe6740e9b76"}, -] -lxml = [ - {file = "lxml-4.5.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0701f7965903a1c3f6f09328c1278ac0eee8f56f244e66af79cb224b7ef3801c"}, - {file = "lxml-4.5.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:06d4e0bbb1d62e38ae6118406d7cdb4693a3fa34ee3762238bcb96c9e36a93cd"}, - {file = "lxml-4.5.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5828c7f3e615f3975d48f40d4fe66e8a7b25f16b5e5705ffe1d22e43fb1f6261"}, - {file = "lxml-4.5.0-cp27-cp27m-win32.whl", hash = "sha256:afdb34b715daf814d1abea0317b6d672476b498472f1e5aacbadc34ebbc26e89"}, - {file = "lxml-4.5.0-cp27-cp27m-win_amd64.whl", hash = "sha256:585c0869f75577ac7a8ff38d08f7aac9033da2c41c11352ebf86a04652758b7a"}, - {file = "lxml-4.5.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:8a0ebda56ebca1a83eb2d1ac266649b80af8dd4b4a3502b2c1e09ac2f88fe128"}, - {file = "lxml-4.5.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:fe976a0f1ef09b3638778024ab9fb8cde3118f203364212c198f71341c0715ca"}, - {file = "lxml-4.5.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7bc1b221e7867f2e7ff1933165c0cec7153dce93d0cdba6554b42a8beb687bdb"}, - {file = "lxml-4.5.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d068f55bda3c2c3fcaec24bd083d9e2eede32c583faf084d6e4b9daaea77dde8"}, - {file = "lxml-4.5.0-cp35-cp35m-win32.whl", hash = "sha256:e4aa948eb15018a657702fee0b9db47e908491c64d36b4a90f59a64741516e77"}, - {file = "lxml-4.5.0-cp35-cp35m-win_amd64.whl", hash = "sha256:1f2c4ec372bf1c4a2c7e4bb20845e8bcf8050365189d86806bad1e3ae473d081"}, - {file = "lxml-4.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:5d467ce9c5d35b3bcc7172c06320dddb275fea6ac2037f72f0a4d7472035cea9"}, - {file = "lxml-4.5.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:95e67224815ef86924fbc2b71a9dbd1f7262384bca4bc4793645794ac4200717"}, - {file = "lxml-4.5.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ebec08091a22c2be870890913bdadd86fcd8e9f0f22bcb398abd3af914690c15"}, - {file = "lxml-4.5.0-cp36-cp36m-win32.whl", hash = "sha256:deadf4df349d1dcd7b2853a2c8796593cc346600726eff680ed8ed11812382a7"}, - {file = "lxml-4.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f2b74784ed7e0bc2d02bd53e48ad6ba523c9b36c194260b7a5045071abbb1012"}, - {file = "lxml-4.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fa071559f14bd1e92077b1b5f6c22cf09756c6de7139370249eb372854ce51e6"}, - {file = "lxml-4.5.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:edc15fcfd77395e24543be48871c251f38132bb834d9fdfdad756adb6ea37679"}, - {file = "lxml-4.5.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd52e796fee7171c4361d441796b64df1acfceb51f29e545e812f16d023c4bbc"}, - {file = "lxml-4.5.0-cp37-cp37m-win32.whl", hash = "sha256:90ed0e36455a81b25b7034038e40880189169c308a3df360861ad74da7b68c1a"}, - {file = "lxml-4.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:df533af6f88080419c5a604d0d63b2c33b1c0c4409aba7d0cb6de305147ea8c8"}, - {file = "lxml-4.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4b2c63cc7963aedd08a5f5a454c9f67251b1ac9e22fd9d72836206c42dc2a72"}, - {file = "lxml-4.5.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e5d842c73e4ef6ed8c1bd77806bf84a7cb535f9c0cf9b2c74d02ebda310070e1"}, - {file = "lxml-4.5.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:63dbc21efd7e822c11d5ddbedbbb08cd11a41e0032e382a0fd59b0b08e405a3a"}, - {file = "lxml-4.5.0-cp38-cp38-win32.whl", hash = "sha256:4235bc124fdcf611d02047d7034164897ade13046bda967768836629bc62784f"}, - {file = "lxml-4.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5b3c4b7edd2e770375a01139be11307f04341ec709cf724e0f26ebb1eef12c3"}, - {file = "lxml-4.5.0.tar.gz", hash = "sha256:8620ce80f50d023d414183bf90cc2576c2837b88e00bea3f33ad2630133bbb60"}, -] -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"}, -] -packaging = [ - {file = "packaging-20.3-py2.py3-none-any.whl", hash = "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"}, - {file = "packaging-20.3.tar.gz", hash = "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3"}, -] -psycopg2-binary = [ - {file = "psycopg2-binary-2.8.5.tar.gz", hash = "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6"}, - {file = "psycopg2_binary-2.8.5-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:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:08507efbe532029adee21b8d4c999170a83760d38249936038bd0602327029b5"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b9a8b391c2b0321e0cd7ec6b4cfcc3dd6349347bd1207d48bcb752aa6c553a66"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-win32.whl", hash = "sha256:3286541b9d85a340ee4ed42732d15fc1bb441dc500c97243a768154ab8505bb5"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-win_amd64.whl", hash = "sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ba13346ff6d3eb2dca0b6fa0d8a9d999eff3dcd9b55f3a890f12b0b6362b2b38"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c8830b7d5f16fd79d39b21e3d94f247219036b29b30c8270314c46bf8b732389"}, - {file = "psycopg2_binary-2.8.5-cp34-cp34m-win32.whl", hash = "sha256:51f7823f1b087d2020d8e8c9e6687473d3d239ba9afc162d9b2ab6e80b53f9f9"}, - {file = "psycopg2_binary-2.8.5-cp34-cp34m-win_amd64.whl", hash = "sha256:107d9be3b614e52a192719c6bf32e8813030020ea1d1215daa86ded9a24d8b04"}, - {file = "psycopg2_binary-2.8.5-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:930315ac53dc65cbf52ab6b6d27422611f5fb461d763c531db229c7e1af6c0b3"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6bb2dd006a46a4a4ce95201f836194eb6a1e863f69ee5bab506673e0ca767057"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:3939cf75fc89c5e9ed836e228c4a63604dff95ad19aed2bbf71d5d04c15ed5ce"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-win32.whl", hash = "sha256:a20299ee0ea2f9cca494396ac472d6e636745652a64a418b39522c120fd0a0a4"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-win_amd64.whl", hash = "sha256:cc30cb900f42c8a246e2cb76539d9726f407330bc244ca7729c41a44e8d807fb"}, - {file = "psycopg2_binary-2.8.5-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:40abc319f7f26c042a11658bf3dd3b0b3bceccf883ec1c565d5c909a90204434"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:702f09d8f77dc4794651f650828791af82f7c2efd8c91ae79e3d9fe4bb7d4c98"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d1a8b01f6a964fec702d6b6dac1f91f2b9f9fe41b310cbb16c7ef1fac82df06d"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-win32.whl", hash = "sha256:17a0ea0b0eabf07035e5e0d520dabc7950aeb15a17c6d36128ba99b2721b25b1"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:e004db88e5a75e5fdab1620fb9f90c9598c2a195a594225ac4ed2a6f1c23e162"}, - {file = "psycopg2_binary-2.8.5-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:a34826d6465c2e2bbe9d0605f944f19d2480589f89863ed5f091943be27c9de4"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cac918cd7c4c498a60f5d2a61d4f0a6091c2c9490d81bc805c963444032d0dab"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7b832d76cc65c092abd9505cc670c4e3421fd136fb6ea5b94efbe4c146572505"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-win32.whl", hash = "sha256:bb0608694a91db1e230b4a314e8ed00ad07ed0c518f9a69b83af2717e31291a3"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:eb2f43ae3037f1ef5e19339c41cf56947021ac892f668765cd65f8ab9814192e"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:07cf82c870ec2d2ce94d18e70c13323c89f2f2a2628cbf1feee700630be2519a"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a69970ee896e21db4c57e398646af9edc71c003bc52a3cc77fb150240fefd266"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7036ccf715925251fac969f4da9ad37e4b7e211b1e920860148a10c0de963522"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-win32.whl", hash = "sha256:8f74e631b67482d504d7e9cf364071fc5d54c28e79a093ff402d5f8f81e23bfa"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd"}, -] -pyflakes = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, -] -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.4.1.tar.gz", hash = "sha256:2366c7aa373118315de7c082401907bacd28e8b1e4e0a6d702334d17b89e71aa"}, -] -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-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"}, - {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, -] -requests = [ - {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, - {file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"}, -] -"ruamel.yaml" = [ - {file = "ruamel.yaml-0.16.10-py2.py3-none-any.whl", hash = "sha256:0962fd7999e064c4865f96fb1e23079075f4a2a14849bcdc5cdba53a24f9759b"}, - {file = "ruamel.yaml-0.16.10.tar.gz", hash = "sha256:099c644a778bf72ffa00524f78dd0b6476bca94a1da344130f4bf3381ce5b954"}, -] -"ruamel.yaml.clib" = [ - {file = "ruamel.yaml.clib-0.2.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9c6d040d0396c28d3eaaa6cb20152cb3b2f15adf35a0304f4f40a3cf9f1d2448"}, - {file = "ruamel.yaml.clib-0.2.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4d55386129291b96483edcb93b381470f7cd69f97585829b048a3d758d31210a"}, - {file = "ruamel.yaml.clib-0.2.0-cp27-cp27m-win32.whl", hash = "sha256:8073c8b92b06b572e4057b583c3d01674ceaf32167801fe545a087d7a1e8bf52"}, - {file = "ruamel.yaml.clib-0.2.0-cp27-cp27m-win_amd64.whl", hash = "sha256:615b0396a7fad02d1f9a0dcf9f01202bf9caefee6265198f252c865f4227fcc6"}, - {file = "ruamel.yaml.clib-0.2.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:a0ff786d2a7dbe55f9544b3f6ebbcc495d7e730df92a08434604f6f470b899c5"}, - {file = "ruamel.yaml.clib-0.2.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:ea4362548ee0cbc266949d8a441238d9ad3600ca9910c3fe4e82ee3a50706973"}, - {file = "ruamel.yaml.clib-0.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:77556a7aa190be9a2bd83b7ee075d3df5f3c5016d395613671487e79b082d784"}, - {file = "ruamel.yaml.clib-0.2.0-cp35-cp35m-win32.whl", hash = "sha256:392b7c371312abf27fb549ec2d5e0092f7ef6e6c9f767bfb13e83cb903aca0fd"}, - {file = "ruamel.yaml.clib-0.2.0-cp35-cp35m-win_amd64.whl", hash = "sha256:ed5b3698a2bb241b7f5cbbe277eaa7fe48b07a58784fba4f75224fd066d253ad"}, - {file = "ruamel.yaml.clib-0.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7aee724e1ff424757b5bd8f6c5bbdb033a570b2b4683b17ace4dbe61a99a657b"}, - {file = "ruamel.yaml.clib-0.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d0d3ac228c9bbab08134b4004d748cf9f8743504875b3603b3afbb97e3472947"}, - {file = "ruamel.yaml.clib-0.2.0-cp36-cp36m-win32.whl", hash = "sha256:f9dcc1ae73f36e8059589b601e8e4776b9976effd76c21ad6a855a74318efd6e"}, - {file = "ruamel.yaml.clib-0.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e77424825caba5553bbade750cec2277ef130647d685c2b38f68bc03453bac6"}, - {file = "ruamel.yaml.clib-0.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d10e9dd744cf85c219bf747c75194b624cc7a94f0c80ead624b06bfa9f61d3bc"}, - {file = "ruamel.yaml.clib-0.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:550168c02d8de52ee58c3d8a8193d5a8a9491a5e7b2462d27ac5bf63717574c9"}, - {file = "ruamel.yaml.clib-0.2.0-cp37-cp37m-win32.whl", hash = "sha256:57933a6986a3036257ad7bf283529e7c19c2810ff24c86f4a0cfeb49d2099919"}, - {file = "ruamel.yaml.clib-0.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b1b7fcee6aedcdc7e62c3a73f238b3d080c7ba6650cd808bce8d7761ec484070"}, - {file = "ruamel.yaml.clib-0.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:be018933c2f4ee7de55e7bd7d0d801b3dfb09d21dad0cce8a97995fd3e44be30"}, - {file = "ruamel.yaml.clib-0.2.0.tar.gz", hash = "sha256:b66832ea8077d9b3f6e311c4a53d06273db5dc2db6e8a908550f3c14d67e718c"}, -] -sentry-sdk = [ - {file = "sentry-sdk-0.15.1.tar.gz", hash = "sha256:3ac0c430761b3cb7682ce612151d829f8644bb3830d4e530c75b02ceb745ff49"}, - {file = "sentry_sdk-0.15.1-py2.py3-none-any.whl", hash = "sha256:06825c15a78934e78941ea25910db71314c891608a46492fc32c15902c6b2119"}, -] -six = [ - {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, - {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, -] -soupsieve = [ - {file = "soupsieve-1.9.5-py2.py3-none-any.whl", hash = "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5"}, - {file = "soupsieve-1.9.5.tar.gz", hash = "sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda"}, -] -sqlparse = [ - {file = "sqlparse-0.3.1-py2.py3-none-any.whl", hash = "sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e"}, - {file = "sqlparse-0.3.1.tar.gz", hash = "sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"}, -] -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.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, - {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, - {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, -] -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.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"}, - {file = "urllib3-1.25.8.tar.gz", hash = "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"}, -] -vine = [ - {file = "vine-1.3.0-py2.py3-none-any.whl", hash = "sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"}, - {file = "vine-1.3.0.tar.gz", hash = "sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87"}, -] -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.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, - {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, -] diff --git a/pyproject.toml b/pyproject.toml index bdc34a9..e62c754 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,42 +1,81 @@ -[tool.poetry] +[project] name = "newsreader" -version = "0.2" -description = "Webapplication for reading RSS feeds" -authors = ["Sonny "] -license = "GPL-3.0" +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", +] -[tool.poetry.dependencies] -python = "^3.7" -bleach = "^3.1.4" -Django = "^3.0.5" -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" -django = ">=3.0.7" -sentry-sdk = "^0.15.1" +[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.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" +[project.optional-dependencies] +sentry = ["sentry-sdk~=2.0"] -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +[tool.uv] +environments = ["sys_platform == 'linux'"] +default-groups = ["test-tools"] + +[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" +] diff --git a/src/manage.py b/src/manage.py index 45fc02f..dc8edeb 100755 --- a/src/manage.py +++ b/src/manage.py @@ -1,11 +1,12 @@ #!/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.dev") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.docker") 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 49390c7..8ae55cb 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 ugettext as _ +from django.utils.translation import gettext as _ from newsreader.accounts.models import User @@ -11,8 +11,6 @@ class UserAdminForm(UserChangeForm): class Meta: widgets = { "email": forms.EmailInput(attrs={"size": "50"}), - "reddit_access_token": forms.TextInput(attrs={"size": "90"}), - "reddit_refresh_token": forms.TextInput(attrs={"size": "90"}), } @@ -30,10 +28,6 @@ class UserAdmin(DjangoUserAdmin): _("User settings"), {"fields": ("email", "password", "first_name", "last_name", "is_active")}, ), - ( - _("Reddit settings"), - {"fields": ("reddit_access_token", "reddit_refresh_token")}, - ), ( _("Permission settings"), {"classes": ("collapse",), "fields": ("is_staff", "is_superuser")}, diff --git a/src/newsreader/accounts/apps.py b/src/newsreader/accounts/apps.py index fb0257e..df906cf 100644 --- a/src/newsreader/accounts/apps.py +++ b/src/newsreader/accounts/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class AccountsConfig(AppConfig): - name = "accounts" + name = "newsreader.accounts" diff --git a/src/newsreader/accounts/forms.py b/src/newsreader/accounts/forms.py index 7a29f99..6247eff 100644 --- a/src/newsreader/accounts/forms.py +++ b/src/newsreader/accounts/forms.py @@ -1,9 +1,11 @@ from django import forms from newsreader.accounts.models import User +from newsreader.core.forms import CheckboxInput class UserSettingsForm(forms.ModelForm): class Meta: model = User - fields = ("first_name", "last_name") + fields = ("first_name", "last_name", "auto_mark_read") + widgets = {"auto_mark_read": CheckboxInput} diff --git a/src/newsreader/accounts/migrations/0001_initial.py b/src/newsreader/accounts/migrations/0001_initial.py index 17b5729..6b22977 100644 --- a/src/newsreader/accounts/migrations/0001_initial.py +++ b/src/newsreader/accounts/migrations/0001_initial.py @@ -8,7 +8,6 @@ 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 b6848a3..41ed65e 100644 --- a/src/newsreader/accounts/migrations/0002_remove_user_username.py +++ b/src/newsreader/accounts/migrations/0002_remove_user_username.py @@ -4,7 +4,6 @@ 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 3d55f65..1f024cf 100644 --- a/src/newsreader/accounts/migrations/0003_auto_20190714_1417.py +++ b/src/newsreader/accounts/migrations/0003_auto_20190714_1417.py @@ -6,7 +6,6 @@ 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 69a78e3..6f8b5d2 100644 --- a/src/newsreader/accounts/migrations/0004_auto_20190714_1501.py +++ b/src/newsreader/accounts/migrations/0004_auto_20190714_1501.py @@ -4,7 +4,6 @@ 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 262ed44..50746fe 100644 --- a/src/newsreader/accounts/migrations/0005_remove_user_task_interval.py +++ b/src/newsreader/accounts/migrations/0005_remove_user_task_interval.py @@ -4,7 +4,6 @@ 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 2afd7c4..de98048 100644 --- a/src/newsreader/accounts/migrations/0006_auto_20191116_1253.py +++ b/src/newsreader/accounts/migrations/0006_auto_20191116_1253.py @@ -6,7 +6,6 @@ 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 eb1204a..4425d06 100644 --- a/src/newsreader/accounts/migrations/0007_auto_20191116_1255.py +++ b/src/newsreader/accounts/migrations/0007_auto_20191116_1255.py @@ -6,7 +6,6 @@ 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 657245a..98923fc 100644 --- a/src/newsreader/accounts/migrations/0008_auto_20200422_2243.py +++ b/src/newsreader/accounts/migrations/0008_auto_20200422_2243.py @@ -15,7 +15,6 @@ 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 3b01b0f..e407f1b 100644 --- a/src/newsreader/accounts/migrations/0009_auto_20200524_1218.py +++ b/src/newsreader/accounts/migrations/0009_auto_20200524_1218.py @@ -6,7 +6,6 @@ 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 294ff31..4c1229e 100644 --- a/src/newsreader/accounts/migrations/0010_auto_20200603_2230.py +++ b/src/newsreader/accounts/migrations/0010_auto_20200603_2230.py @@ -4,7 +4,6 @@ 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 new file mode 100644 index 0000000..c2e7d7b --- /dev/null +++ b/src/newsreader/accounts/migrations/0011_auto_20200913_2101.py @@ -0,0 +1,20 @@ +# Generated by Django 3.0.7 on 2020-09-13 19:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("accounts", "0010_auto_20200603_2230")] + + operations = [ + migrations.AddField( + model_name="user", + name="twitter_oauth_token", + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name="user", + name="twitter_oauth_token_secret", + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/src/newsreader/accounts/migrations/0012_remove_user_task.py b/src/newsreader/accounts/migrations/0012_remove_user_task.py new file mode 100644 index 0000000..56aab08 --- /dev/null +++ b/src/newsreader/accounts/migrations/0012_remove_user_task.py @@ -0,0 +1,9 @@ +# Generated by Django 3.0.7 on 2020-09-26 15:34 + +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 new file mode 100644 index 0000000..9d56d1c --- /dev/null +++ b/src/newsreader/accounts/migrations/0013_user_auto_mark_read.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.7 on 2020-10-27 21:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("accounts", "0012_remove_user_task")] + + operations = [ + migrations.AddField( + model_name="user", + name="auto_mark_read", + field=models.BooleanField( + default=True, + help_text="Wether posts should be marked as read after x amount of seconds of reading", + verbose_name="Auto read marking", + ), + ) + ] diff --git a/src/newsreader/accounts/migrations/0014_auto_20201218_2216.py b/src/newsreader/accounts/migrations/0014_auto_20201218_2216.py new file mode 100644 index 0000000..40b7181 --- /dev/null +++ b/src/newsreader/accounts/migrations/0014_auto_20201218_2216.py @@ -0,0 +1,48 @@ +# Generated by Django 3.0.7 on 2020-12-18 21:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("accounts", "0013_user_auto_mark_read")] + + operations = [ + migrations.AddField( + model_name="user", + name="reddit_allow_nfsw", + field=models.BooleanField(default=False, verbose_name="Allow NSFW posts"), + ), + migrations.AddField( + model_name="user", + name="reddit_allow_spoiler", + field=models.BooleanField(default=False, verbose_name="Allow spoilers"), + ), + migrations.AddField( + model_name="user", + name="reddit_allow_viewed", + field=models.BooleanField( + default=True, verbose_name="Allow already seen posts" + ), + ), + migrations.AddField( + model_name="user", + name="reddit_comments_min", + field=models.PositiveIntegerField( + default=0, verbose_name="Minimum amount of comments" + ), + ), + migrations.AddField( + model_name="user", + name="reddit_downvotes_max", + field=models.PositiveIntegerField( + default=0, verbose_name="Maximum amount of downvotes" + ), + ), + migrations.AddField( + model_name="user", + name="reddit_upvotes_min", + field=models.PositiveIntegerField( + default=0, verbose_name="Minimum amount of upvotes" + ), + ), + ] diff --git a/src/newsreader/accounts/migrations/0015_auto_20201219_1330.py b/src/newsreader/accounts/migrations/0015_auto_20201219_1330.py new file mode 100644 index 0000000..989fe26 --- /dev/null +++ b/src/newsreader/accounts/migrations/0015_auto_20201219_1330.py @@ -0,0 +1,16 @@ +# Generated by Django 3.0.7 on 2020-12-19 12:30 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [("accounts", "0014_auto_20201218_2216")] + + operations = [ + migrations.RemoveField(model_name="user", name="reddit_allow_nfsw"), + migrations.RemoveField(model_name="user", name="reddit_allow_spoiler"), + migrations.RemoveField(model_name="user", name="reddit_allow_viewed"), + migrations.RemoveField(model_name="user", name="reddit_comments_min"), + migrations.RemoveField(model_name="user", name="reddit_downvotes_max"), + migrations.RemoveField(model_name="user", name="reddit_upvotes_min"), + ] diff --git a/src/newsreader/accounts/migrations/0016_alter_user_first_name.py b/src/newsreader/accounts/migrations/0016_alter_user_first_name.py new file mode 100644 index 0000000..a5fade3 --- /dev/null +++ b/src/newsreader/accounts/migrations/0016_alter_user_first_name.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2 on 2021-04-23 20:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("accounts", "0015_auto_20201219_1330")] + + operations = [ + migrations.AlterField( + model_name="user", + name="first_name", + field=models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ) + ] diff --git a/src/newsreader/accounts/migrations/0017_auto_20240906_0914.py b/src/newsreader/accounts/migrations/0017_auto_20240906_0914.py new file mode 100644 index 0000000..b3b2034 --- /dev/null +++ b/src/newsreader/accounts/migrations/0017_auto_20240906_0914.py @@ -0,0 +1,20 @@ +# 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 new file mode 100644 index 0000000..cf8816b --- /dev/null +++ b/src/newsreader/accounts/migrations/0018_remove_user_reddit_access_token_and_more.py @@ -0,0 +1,20 @@ +# 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 b8aaa64..15cc97e 100644 --- a/src/newsreader/accounts/models.py +++ b/src/newsreader/accounts/models.py @@ -1,11 +1,9 @@ -import json - from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import UserManager as DjangoUserManager from django.db import models from django.utils.translation import gettext as _ -from django_celery_beat.models import IntervalSchedule, PeriodicTask +from django_celery_beat.models import PeriodicTask class UserManager(DjangoUserManager): @@ -41,18 +39,15 @@ class UserManager(DjangoUserManager): class User(AbstractUser): email = models.EmailField(_("email address"), unique=True) - task = models.OneToOneField( - PeriodicTask, - on_delete=models.CASCADE, - null=True, - blank=True, - editable=False, - verbose_name="collection task", + # settings + auto_mark_read = models.BooleanField( + _("Auto read marking"), + default=True, + help_text=_( + "Wether posts should be marked as read after x amount of seconds of reading" + ), ) - reddit_refresh_token = models.CharField(max_length=255, blank=True, null=True) - reddit_access_token = models.CharField(max_length=255, blank=True, null=True) - username = None objects = UserManager() @@ -60,24 +55,8 @@ class User(AbstractUser): USERNAME_FIELD = "email" REQUIRED_FIELDS = [] - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - - if not self.task: - task_interval, _ = IntervalSchedule.objects.get_or_create( - every=1, period=IntervalSchedule.HOURS - ) - - self.task, _ = PeriodicTask.objects.get_or_create( - enabled=True, - interval=task_interval, - name=f"{self.email}-collection-task", - task="FeedTask", - args=json.dumps([self.pk]), - ) - - self.save() - def delete(self, *args, **kwargs): - self.task.delete() + tasks = PeriodicTask.objects.filter(name__contains=self.email) + tasks.delete() + return super().delete(*args, **kwargs) diff --git a/src/newsreader/accounts/templates/accounts/components/settings-form.html b/src/newsreader/accounts/templates/accounts/components/settings-form.html index 7942354..6e81e79 100644 --- a/src/newsreader/accounts/templates/accounts/components/settings-form.html +++ b/src/newsreader/accounts/templates/accounts/components/settings-form.html @@ -2,29 +2,23 @@ {% load i18n %} {% block actions %} -
-
- {% include "components/form/cancel-button.html" %} -
+
+
+ {% include "components/form/confirm-button.html" %} -
- - {% trans "Change password" %} - + + {% trans "Change password" %} + - {% include "components/form/confirm-button.html" %} - - {% if reddit_authorization_url %} - - {% trans "Authorize Reddit account" %} - - {% endif %} - - {% if reddit_refresh_url %} - - {% trans "Refresh Reddit access token" %} - - {% endif %} -
-
+ {% if favicon_task_allowed %} + + {% trans "Fetch favicons" %} + + {% else %} + + {% endif %} + +
{% endblock actions %} diff --git a/src/newsreader/accounts/templates/accounts/views/login.html b/src/newsreader/accounts/templates/accounts/views/login.html index b4c391d..b83a4dd 100644 --- a/src/newsreader/accounts/templates/accounts/views/login.html +++ b/src/newsreader/accounts/templates/accounts/views/login.html @@ -1,7 +1,9 @@ -{% extends "base.html" %} +{% extends "sidebar.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 fb8a98b..1995b97 100644 --- a/src/newsreader/accounts/templates/accounts/views/password-change.html +++ b/src/newsreader/accounts/templates/accounts/views/password-change.html @@ -1,8 +1,12 @@ -{% extends "base.html" %} +{% extends "sidebar.html" %} + {% block content %} -
- {% url 'accounts:settings' 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 deleted file mode 100644 index b393bbe..0000000 --- a/src/newsreader/accounts/templates/accounts/views/reddit.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -
-
- {% if error %} -

Reddit authorization failed

-

{{ error }}

- {% elif access_token and refresh_token %} -

Reddit account is linked

-

Your reddit account was successfully linked.

- {% endif %} - -

Return to settings page

-
-
-{% endblock %} diff --git a/src/newsreader/accounts/templates/accounts/views/settings.html b/src/newsreader/accounts/templates/accounts/views/settings.html index bf01f8e..590fa0f 100644 --- a/src/newsreader/accounts/templates/accounts/views/settings.html +++ b/src/newsreader/accounts/templates/accounts/views/settings.html @@ -1,7 +1,9 @@ -{% extends "base.html" %} +{% extends "sidebar.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/tests/factories.py b/src/newsreader/accounts/tests/factories.py index fc13d74..746db80 100644 --- a/src/newsreader/accounts/tests/factories.py +++ b/src/newsreader/accounts/tests/factories.py @@ -5,8 +5,6 @@ from django.utils.crypto import get_random_string import factory -from registration.models import RegistrationProfile - from newsreader.accounts.models import User @@ -29,11 +27,3 @@ 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 deleted file mode 100644 index 45d0909..0000000 --- a/src/newsreader/accounts/tests/test_activation.py +++ /dev/null @@ -1,99 +0,0 @@ -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_favicon.py b/src/newsreader/accounts/tests/test_favicon.py new file mode 100644 index 0000000..d3eb56b --- /dev/null +++ b/src/newsreader/accounts/tests/test_favicon.py @@ -0,0 +1,37 @@ +from unittest.mock import patch + +from django.core.cache import cache +from django.test import TestCase +from django.urls import reverse + +from newsreader.accounts.tests.factories import UserFactory + + +class FaviconRedirectViewTestCase(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.favicon.FaviconTask") + self.mocked_task = self.patch.start() + + def tearDown(self): + cache.clear() + + def test_simple(self): + response = self.client.get(reverse("accounts:settings:favicon")) + + self.assertRedirects(response, reverse("accounts:settings:home")) + + self.mocked_task.delay.assert_called_once_with(self.user.pk) + + self.assertEqual(1, cache.get(f"{self.user.email}-favicon-task")) + + def test_not_active(self): + cache.set(f"{self.user.email}-favicon-task", 1) + + response = self.client.get(reverse("accounts:settings:favicon")) + + self.assertRedirects(response, reverse("accounts:settings:home")) + + self.mocked_task.delay.assert_not_called() diff --git a/src/newsreader/accounts/tests/test_registration.py b/src/newsreader/accounts/tests/test_registration.py deleted file mode 100644 index 27c87bf..0000000 --- a/src/newsreader/accounts/tests/test_registration.py +++ /dev/null @@ -1,110 +0,0 @@ -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 deleted file mode 100644 index 0209f94..0000000 --- a/src/newsreader/accounts/tests/test_resend_activation.py +++ /dev/null @@ -1,77 +0,0 @@ -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/test_settings.py b/src/newsreader/accounts/tests/test_settings.py index d093ea4..5a12637 100644 --- a/src/newsreader/accounts/tests/test_settings.py +++ b/src/newsreader/accounts/tests/test_settings.py @@ -1,14 +1,8 @@ -from unittest.mock import 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 newsreader.accounts.models import User from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.exceptions import StreamTooManyException class SettingsViewTestCase(TestCase): @@ -16,146 +10,22 @@ class SettingsViewTestCase(TestCase): self.user = UserFactory(email="test@test.nl", password="test") self.client.force_login(self.user) - self.url = reverse("accounts:settings") + self.url = reverse("accounts:settings:home") def test_simple(self): response = self.client.get(self.url) self.assertEquals(response.status_code, 200) - self.assertContains(response, "Authorize Reddit account") def test_user_credential_change(self): response = self.client.post( - reverse("accounts:settings"), + reverse("accounts:settings:home"), {"first_name": "First name", "last_name": "Last name"}, ) user = User.objects.get() - self.assertRedirects(response, reverse("accounts:settings")) + self.assertRedirects(response, reverse("accounts:settings:home")) self.assertEquals(user.first_name, "First name") self.assertEquals(user.last_name, "Last name") - - def test_linked_reddit_account(self): - self.user.reddit_refresh_token = "test" - self.user.save() - - response = self.client.get(self.url) - - self.assertEquals(response.status_code, 200) - self.assertNotContains(response, "Authorize Reddit account") - - -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: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 settings 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) diff --git a/src/newsreader/accounts/tests/tests.py b/src/newsreader/accounts/tests/tests.py index e28dbd3..3a1ba4f 100644 --- a/src/newsreader/accounts/tests/tests.py +++ b/src/newsreader/accounts/tests/tests.py @@ -1,22 +1,21 @@ from django.test import TestCase -from django_celery_beat.models import PeriodicTask +from django_celery_beat.models import IntervalSchedule, PeriodicTask -from newsreader.accounts.models import User +from newsreader.accounts.tests.factories import UserFactory class UserTestCase(TestCase): - def test_task_is_created(self): - user = User.objects.create(email="durp@burp.nl", task=None) - task = PeriodicTask.objects.get(name=f"{user.email}-collection-task") - - user.refresh_from_db() - - self.assertEquals(task, user.task) - self.assertEquals(PeriodicTask.objects.count(), 1) - def test_task_is_deleted(self): - user = User.objects.create(email="durp@burp.nl", task=None) + user = UserFactory(email="durp@burp.nl") + + interval = IntervalSchedule.objects.create( + every=1, period=IntervalSchedule.HOURS + ) + PeriodicTask.objects.create( + name=f"{user.email}-feed", task="FeedTask", interval=interval + ) + user.delete() self.assertEquals(PeriodicTask.objects.count(), 0) diff --git a/src/newsreader/accounts/urls.py b/src/newsreader/accounts/urls.py index 672cf6d..18a9b21 100644 --- a/src/newsreader/accounts/urls.py +++ b/src/newsreader/accounts/urls.py @@ -1,10 +1,8 @@ from django.contrib.auth.decorators import login_required -from django.urls import path +from django.urls import include, path from newsreader.accounts.views import ( - ActivationCompleteView, - ActivationResendView, - ActivationView, + FaviconRedirectView, LoginView, LogoutView, PasswordChangeView, @@ -12,35 +10,21 @@ from newsreader.accounts.views import ( PasswordResetConfirmView, PasswordResetDoneView, PasswordResetView, - RedditTemplateView, - RedditTokenRedirectView, - RegistrationClosedView, - RegistrationCompleteView, - RegistrationView, SettingsView, ) +settings_patterns = [ + # Misc + path("favicon/", login_required(FaviconRedirectView.as_view()), name="favicon"), + path("", login_required(SettingsView.as_view()), name="home"), +] + urlpatterns = [ + # Auth path("login/", LoginView.as_view(), name="login"), path("logout/", LogoutView.as_view(), name="logout"), - 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( "password-reset/done/", @@ -62,15 +46,6 @@ urlpatterns = [ login_required(PasswordChangeView.as_view()), name="password-change", ), - path("settings/", login_required(SettingsView.as_view()), name="settings"), - path( - "settings/reddit/callback/", - login_required(RedditTemplateView.as_view()), - name="reddit-template", - ), - path( - "settings/reddit/refresh/", - login_required(RedditTokenRedirectView.as_view()), - name="reddit-refresh", - ), + # Settings + path("settings/", include((settings_patterns, "settings"))), ] diff --git a/src/newsreader/accounts/views.py b/src/newsreader/accounts/views.py deleted file mode 100644 index 4f982a9..0000000 --- a/src/newsreader/accounts/views.py +++ /dev/null @@ -1,210 +0,0 @@ -from django.contrib import messages -from django.contrib.auth import views as django_views -from django.core.cache import cache -from django.shortcuts import render -from django.urls import reverse_lazy -from django.utils.translation import gettext as _ -from django.views.generic import RedirectView, TemplateView -from django.views.generic.edit import FormView, ModelFormMixin - -from registration.backends.default import views as registration_views - -from newsreader.accounts.forms import UserSettingsForm -from newsreader.accounts.models import User -from newsreader.news.collection.exceptions import StreamException -from newsreader.news.collection.reddit import ( - get_reddit_access_token, - get_reddit_authorization_url, -) -from newsreader.news.collection.tasks import RedditTokenTask - - -class LoginView(django_views.LoginView): - template_name = "accounts/views/login.html" - success_url = reverse_lazy("index") - - -class LogoutView(django_views.LogoutView): - next_page = reverse_lazy("accounts:login") - - -# 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 - ) - - -# PasswordResetView sends the mail -# PasswordResetDoneView shows a success message for the above -# PasswordResetConfirmView checks the link the user clicked and -# prompts for a new password -# PasswordResetCompleteView shows a success message for the above -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(django_views.PasswordResetDoneView): - template_name = "password-reset/password-reset-done.html" - - -class PasswordResetConfirmView(django_views.PasswordResetConfirmView): - template_name = "password-reset/password-reset-confirm.html" - success_url = reverse_lazy("accounts:password-reset-complete") - - -class PasswordResetCompleteView(django_views.PasswordResetCompleteView): - template_name = "password-reset/password-reset-complete.html" - - -class PasswordChangeView(django_views.PasswordChangeView): - template_name = "accounts/views/password-change.html" - success_url = reverse_lazy("accounts:settings") - - -class SettingsView(ModelFormMixin, FormView): - template_name = "accounts/views/settings.html" - success_url = reverse_lazy("accounts:settings") - form_class = UserSettingsForm - model = User - - def get(self, request, *args, **kwargs): - self.object = self.get_object() - return super().get(request, *args, **kwargs) - - def get_object(self, **kwargs): - return self.request.user - - def get_context_data(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:reddit-refresh") - - if not user.reddit_refresh_token: - reddit_authorization_url = get_reddit_authorization_url(user) - - return { - **super().get_context_data(**kwargs), - "reddit_authorization_url": reddit_authorization_url, - "reddit_refresh_url": reddit_refresh_url, - } - - def get_form_kwargs(self): - return {**super().get_form_kwargs(), "instance": self.request.user} - - -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") - - 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 diff --git a/src/newsreader/accounts/views/__init__.py b/src/newsreader/accounts/views/__init__.py new file mode 100644 index 0000000..d20e6bb --- /dev/null +++ b/src/newsreader/accounts/views/__init__.py @@ -0,0 +1,23 @@ +from newsreader.accounts.views.auth import LoginView, LogoutView +from newsreader.accounts.views.favicon import FaviconRedirectView +from newsreader.accounts.views.password import ( + PasswordChangeView, + PasswordResetCompleteView, + PasswordResetConfirmView, + PasswordResetDoneView, + PasswordResetView, +) +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 new file mode 100644 index 0000000..cef1f61 --- /dev/null +++ b/src/newsreader/accounts/views/auth.py @@ -0,0 +1,13 @@ +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): + template_name = "accounts/views/login.html" + success_url = reverse_lazy("index") + + +class LogoutView(django_views.LogoutView): + next_page = reverse_lazy("accounts:login") diff --git a/src/newsreader/accounts/views/favicon.py b/src/newsreader/accounts/views/favicon.py new file mode 100644 index 0000000..1b85399 --- /dev/null +++ b/src/newsreader/accounts/views/favicon.py @@ -0,0 +1,26 @@ +from django.contrib import messages +from django.core.cache import cache +from django.urls import reverse_lazy +from django.utils.translation import gettext as _ +from django.views.generic import RedirectView + +from newsreader.news.collection.tasks import FaviconTask + + +class FaviconRedirectView(RedirectView): + url = reverse_lazy("accounts:settings:home") + + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + + user = request.user + task_active = cache.get(f"{user.email}-favicon-task") + + if not task_active: + FaviconTask.delay(user.pk) + messages.success(request, _("Favicons are being fetched")) + cache.set(f"{user.email}-favicon-task", 1, 18000) # 5 hours + return response + + messages.error(request, _("Limit reached, try again later")) + return response diff --git a/src/newsreader/accounts/views/password.py b/src/newsreader/accounts/views/password.py new file mode 100644 index 0000000..9f792ec --- /dev/null +++ b/src/newsreader/accounts/views/password.py @@ -0,0 +1,34 @@ +from django.contrib.auth import views as django_views +from django.urls import reverse_lazy + +from newsreader.utils.views import NavListMixin + + +# PasswordResetView sends the mail +# PasswordResetDoneView shows a success message for the above +# 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): + 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): + template_name = "password-reset/password-reset-done.html" + + +class PasswordResetConfirmView(NavListMixin, 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): + template_name = "password-reset/password-reset-complete.html" + + +class PasswordChangeView(NavListMixin, django_views.PasswordChangeView): + template_name = "accounts/views/password-change.html" + success_url = reverse_lazy("accounts:settings") diff --git a/src/newsreader/accounts/views/settings.py b/src/newsreader/accounts/views/settings.py new file mode 100644 index 0000000..7210449 --- /dev/null +++ b/src/newsreader/accounts/views/settings.py @@ -0,0 +1,32 @@ +from django.core.cache import cache +from django.urls import reverse_lazy +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 + + +class SettingsView(NavListMixin, ModelFormMixin, FormView): + template_name = "accounts/views/settings.html" + success_url = reverse_lazy("accounts:settings:home") + form_class = UserSettingsForm + model = User + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + return super().get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + return { + **context, + "favicon_task_allowed": not cache.get(f"{self.request.user}-favicon-task"), + } + + def get_object(self, **kwargs): + return self.request.user + + def get_form_kwargs(self): + return {**super().get_form_kwargs(), "instance": self.request.user} diff --git a/src/newsreader/assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf b/src/newsreader/assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf new file mode 100644 index 0000000..43ed4f5 Binary files /dev/null and b/src/newsreader/assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf differ diff --git a/src/newsreader/assets/fonts/Inter-VariableFont_opsz,wght.ttf b/src/newsreader/assets/fonts/Inter-VariableFont_opsz,wght.ttf new file mode 100644 index 0000000..e31b51e Binary files /dev/null and b/src/newsreader/assets/fonts/Inter-VariableFont_opsz,wght.ttf differ diff --git a/src/newsreader/assets/fonts/METADATA.pb b/src/newsreader/assets/fonts/METADATA.pb deleted file mode 100755 index 18857e1..0000000 --- a/src/newsreader/assets/fonts/METADATA.pb +++ /dev/null @@ -1,101 +0,0 @@ -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 deleted file mode 100755 index 0ffcec9..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-Black.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-BlackItalic.ttf b/src/newsreader/assets/fonts/Rubik-BlackItalic.ttf deleted file mode 100755 index 5bb1d4b..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-BlackItalic.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-Bold.ttf b/src/newsreader/assets/fonts/Rubik-Bold.ttf deleted file mode 100755 index 5493b22..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-Bold.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-BoldItalic.ttf b/src/newsreader/assets/fonts/Rubik-BoldItalic.ttf deleted file mode 100755 index d380dac..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-BoldItalic.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-Italic.ttf b/src/newsreader/assets/fonts/Rubik-Italic.ttf deleted file mode 100755 index cf43a4b..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-Italic.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-Light.ttf b/src/newsreader/assets/fonts/Rubik-Light.ttf deleted file mode 100755 index f6e44cc..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-Light.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-LightItalic.ttf b/src/newsreader/assets/fonts/Rubik-LightItalic.ttf deleted file mode 100755 index b9c5631..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-LightItalic.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-Medium.ttf b/src/newsreader/assets/fonts/Rubik-Medium.ttf deleted file mode 100755 index 5a3f898..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-Medium.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-MediumItalic.ttf b/src/newsreader/assets/fonts/Rubik-MediumItalic.ttf deleted file mode 100755 index 5b5bf1f..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-MediumItalic.ttf and /dev/null differ diff --git a/src/newsreader/assets/fonts/Rubik-Regular.ttf b/src/newsreader/assets/fonts/Rubik-Regular.ttf deleted file mode 100755 index abdc5bc..0000000 Binary files a/src/newsreader/assets/fonts/Rubik-Regular.ttf and /dev/null differ diff --git a/src/newsreader/celery.py b/src/newsreader/celery.py index 3eb59e0..fc9ae73 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.dev") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.docker") 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 43b89fd..220e8d1 100644 --- a/src/newsreader/conf/base.py +++ b/src/newsreader/conf/base.py @@ -1,20 +1,25 @@ -import os +from dotenv import load_dotenv -from pathlib import Path - -from .version import get_current_version +from newsreader.conf.utils import get_env, get_root_dir -BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent -DJANGO_PROJECT_DIR = os.path.join(BASE_DIR, "src", "newsreader") +load_dotenv() -# 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 +try: + from sentry_sdk.integrations.celery import CeleryIntegration + from sentry_sdk.integrations.django import DjangoIntegration +except ImportError: + CeleryIntegration = None + DjangoIntegration = None -ALLOWED_HOSTS = ["127.0.0.1", "localhost"] -INTERNAL_IPS = ["127.0.0.1", "localhost"] + +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"]) # Application definition INSTALLED_APPS = [ @@ -27,10 +32,8 @@ INSTALLED_APPS = [ "django.forms", # third party apps "rest_framework", - "drf_yasg", "celery", "django_celery_beat", - "registration", "axes", # app modules "newsreader.accounts", @@ -40,6 +43,8 @@ INSTALLED_APPS = [ "newsreader.news.collection", ] +SECRET_KEY = get_env("DJANGO_SECRET_KEY", default="") + AUTHENTICATION_BACKENDS = [ "axes.backends.AxesBackend", "django.contrib.auth.backends.ModelBackend", @@ -63,11 +68,10 @@ FORM_RENDERER = "django.forms.renderers.TemplatesSetting" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [os.path.join(DJANGO_PROJECT_DIR, "templates")], + "DIRS": [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", @@ -78,31 +82,30 @@ 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": os.environ.get("POSTGRES_HOST", ""), - "NAME": os.environ.get("POSTGRES_NAME", "newsreader"), - "USER": os.environ.get("POSTGRES_USER"), - "PASSWORD": os.environ.get("POSTGRES_PASSWORD"), + "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=""), } } +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" + CACHES = { "default": { - "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", - "LOCATION": "localhost:11211", + "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", + "LOCATION": "memcached:11211", }, "axes": { - "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", - "LOCATION": "localhost:11211", + "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", + "LOCATION": "memcached:11211", }, } -# Logging -# https://docs.djangoproject.com/en/2.2/topics/logging/#configuring-logging LOGGING = { "version": 1, "disable_existing_loggers": False, @@ -116,66 +119,46 @@ 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", }, - "mail_admins": { - "level": "ERROR", - "filters": ["require_debug_false"], - "class": "django.utils.log.AdminEmailHandler", + "file": { + "level": "DEBUG", + "class": "logging.handlers.RotatingFileHandler", + "filename": BASE_DIR / "logs" / "newsreader.log", + "backupCount": 5, + "maxBytes": 50000000, # 50 mB + "formatter": "timestamped", }, - "syslog": { + "celery": { "level": "INFO", - "filters": ["require_debug_false"], - "class": "logging.handlers.SysLogHandler", - "formatter": "syslog", - "address": "/dev/log", - }, - "syslog_errors": { - "level": "ERROR", - "filters": ["require_debug_false"], - "class": "logging.handlers.SysLogHandler", - "formatter": "syslog", - "address": "/dev/log", + "class": "logging.handlers.RotatingFileHandler", + "filename": BASE_DIR / "logs" / "celery.log", + "backupCount": 5, + "maxBytes": 50000000, # 50 mB + "formatter": "timestamped", }, }, "loggers": { - "django": { - "handlers": ["console", "mail_admins", "syslog_errors"], - "level": "WARNING", - }, + "django": {"handlers": ["console"], "level": "INFO"}, "django.server": { - "handlers": ["console", "syslog_errors"], + "handlers": ["console"], "level": "INFO", "propagate": False, }, - "django.request": { - "handlers": ["console", "syslog_errors"], - "level": "INFO", + "celery.task": {"handlers": ["console", "celery"], "level": "INFO"}, + "newsreader": { + "handlers": ["console", "file"], + "level": "DEBUG", "propagate": False, }, - "celery": {"handlers": ["syslog", "console"], "level": "INFO"}, - "celery.task": { - "handlers": ["syslog", "console"], - "level": "INFO", - "propagate": False, - }, - "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" @@ -190,8 +173,6 @@ 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" @@ -199,27 +180,31 @@ 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 = os.path.join(BASE_DIR, "static") -STATICFILES_DIRS = [os.path.join(DJANGO_PROJECT_DIR, "static")] +STATIC_ROOT = BASE_DIR / "static" +STATICFILES_DIRS = (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", ] -DEFAULT_FROM_EMAIL = "newsreader@rss.fudiggity.nl" +# Email +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" -# Project settings -VERSION = get_current_version() +DEFAULT_FROM_EMAIL = get_env( + "EMAIL_DEFAULT_FROM", required=False, default="webmaster@localhost" +) + +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) -# Reddit integration -REDDIT_CLIENT_ID = "CLIENT_ID" -REDDIT_CLIENT_SECRET = "CLIENT_SECRET" -REDDIT_REDIRECT_URL = "http://127.0.0.1:8000/accounts/settings/reddit/callback/" # Third party settings AXES_HANDLER = "axes.handlers.cache.AxesCacheHandler" @@ -236,7 +221,12 @@ REST_FRAMEWORK = { "rest_framework.permissions.IsAuthenticated", "newsreader.accounts.permissions.IsOwner", ), - "DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",), + "DEFAULT_RENDERER_CLASSES": ( + "djangorestframework_camel_case.render.CamelCaseJSONRenderer", + ), + "DEFAULT_PARSER_CLASSES": ( + "djangorestframework_camel_case.parser.CamelCaseJSONParser", + ), } SWAGGER_SETTINGS = { @@ -247,8 +237,15 @@ 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" -REGISTRATION_OPEN = True -REGISTRATION_AUTO_LOGIN = True -ACCOUNT_ACTIVATION_DAYS = 7 +# Sentry +SENTRY_CONFIG = { + "dsn": get_env("SENTRY_DSN", default="", required=False), + "send_default_pii": False, + "integrations": [DjangoIntegration(), CeleryIntegration()] + if DjangoIntegration and CeleryIntegration + else [], +} diff --git a/src/newsreader/conf/ci.py b/src/newsreader/conf/ci.py new file mode 100644 index 0000000..e69e079 --- /dev/null +++ b/src/newsreader/conf/ci.py @@ -0,0 +1,46 @@ +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 8186db7..57aaff3 100644 --- a/src/newsreader/conf/dev.py +++ b/src/newsreader/conf/dev.py @@ -1,19 +1,35 @@ -from .base import * # isort:skip +from .base import * # noqa: F403 +from .utils import get_current_version SECRET_KEY = "mv4&5#+)-=abz3^&1r^nk_ca6y54--p(4n4cg%z*g&rb64j%wl" -MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] +MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa: F405 EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -INSTALLED_APPS += ["debug_toolbar", "django_extensions"] +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() # 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 dd2471f..3485bf3 100644 --- a/src/newsreader/conf/docker.py +++ b/src/newsreader/conf/docker.py @@ -1,29 +1,43 @@ -from .dev import * # isort:skip +from .base import * # noqa: F403 +from .utils import get_current_version -SECRET_KEY = "=q(ztyo)b6noom#a164g&s9vcj1aawa^g#ing_ir99=_zl4g&$" +DEBUG = True -DATABASES = { - "default": { - "ENGINE": "django.db.backends.postgresql", - "NAME": "newsreader", - "USER": "newsreader", - "PASSWORD": "newsreader", - "HOST": "db", +INSTALLED_APPS += ["debug_toolbar", "django_extensions"] # noqa: F405 + +MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa: F405 + +LOGGING["loggers"].update( # noqa: F405 + { + "celery.task": {"handlers": ["console", "celery"], "level": "DEBUG"}, } -} +) -CACHES = { - "default": { - "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", - "LOCATION": "memcached:11211", - }, - "axes": { - "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", - "LOCATION": "memcached:11211", - }, -} +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -# Celery -# https://docs.celeryproject.org/en/latest/userguide/configuration.html -CELERY_BROKER_URL = "amqp://guest:guest@rabbitmq:5672//" + +TEMPLATES[0]["OPTIONS"]["context_processors"].append( # noqa: F405 + "django.template.context_processors.debug", +) + +# Project settings +VERSION = get_current_version() +ENVIRONMENT = "docker" + +# Third party settings +# Axes +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, "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 deleted file mode 100644 index 67a20dd..0000000 --- a/src/newsreader/conf/gitlab.py +++ /dev/null @@ -1,19 +0,0 @@ -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 bfe9818..e4053ec 100644 --- a/src/newsreader/conf/production.py +++ b/src/newsreader/conf/production.py @@ -1,71 +1,32 @@ -import os +from newsreader.conf.utils import get_env -from dotenv import load_dotenv +from .base import * # noqa: F403 +from .utils import get_current_version -from .base import * # isort:skip - - -load_dotenv() - DEBUG = False -ALLOWED_HOSTS = ["rss.fudiggity.nl"] +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") + ADMINS = [ - ("", email) - for email in os.getenv("ADMINS", "").split(",") - if os.environ.get("ADMINS") + ("", email) for email in get_env("ADMINS", split=",", required=False, default=[]) ] -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", - ] - }, - } -] - -# Reddit integration -REDDIT_CLIENT_ID = os.environ["REDDIT_CLIENT_ID"] -REDDIT_CLIENT_SECRET = os.environ["REDDIT_CLIENT_SECRET"] -REDDIT_REDIRECT_URL = os.environ["REDDIT_CALLBACK_URL"] +# Project settings +VERSION = get_current_version(debug=False) +ENVIRONMENT = "production" # 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_init( - dsn=os.environ.get("SENTRY_DSN"), - integrations=[DjangoIntegration(), CeleryIntegration()], - send_default_pii=False, - release=VERSION, + SENTRY_CONFIG.update( # noqa: F405 + {"release": VERSION, "environment": ENVIRONMENT, "debug": False} ) + + sentry_init(**SENTRY_CONFIG) # noqa: F405 except ImportError: pass diff --git a/src/newsreader/conf/utils.py b/src/newsreader/conf/utils.py new file mode 100644 index 0000000..c46b59d --- /dev/null +++ b/src/newsreader/conf/utils.py @@ -0,0 +1,85 @@ +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 deleted file mode 100644 index f8b4c8d..0000000 --- a/src/newsreader/conf/version.py +++ /dev/null @@ -1,15 +0,0 @@ -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/core/apps.py b/src/newsreader/core/apps.py index 5ef1d60..42f7239 100644 --- a/src/newsreader/core/apps.py +++ b/src/newsreader/core/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class CoreConfig(AppConfig): - name = "core" + name = "newsreader.core" diff --git a/src/newsreader/core/pagination.py b/src/newsreader/core/pagination.py index 5e19771..b44c862 100644 --- a/src/newsreader/core/pagination.py +++ b/src/newsreader/core/pagination.py @@ -1,7 +1,7 @@ -from rest_framework.pagination import PageNumberPagination +from rest_framework import pagination -class ResultSetPagination(PageNumberPagination): +class ResultSetPagination(pagination.PageNumberPagination): page_size_query_param = "count" max_page_size = 50 page_size = 30 @@ -10,3 +10,9 @@ class ResultSetPagination(PageNumberPagination): class LargeResultSetPagination(ResultSetPagination): max_page_size = 100 page_size = 50 + + +class CursorPagination(pagination.CursorPagination): + page_size_query_param = "count" + ordering = "-publication_date" + page_size = 30 diff --git a/src/newsreader/fixtures/default-fixture.json b/src/newsreader/fixtures/default-fixture.json deleted file mode 100644 index 10d6416..0000000 --- a/src/newsreader/fixtures/default-fixture.json +++ /dev/null @@ -1,4023 +0,0 @@ -[ -{ - "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", - "task": 10, - "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_suceeded": "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_suceeded": "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_suceeded": "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_suceeded": "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_suceeded": "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_suceeded": "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_suceeded": "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_suceeded": "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_suceeded": "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_suceeded": "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 new file mode 100644 index 0000000..05e61e7 --- /dev/null +++ b/src/newsreader/fixtures/fixture.json @@ -0,0 +1,1066 @@ +[ + { + "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/fixtures/local/fixture.json b/src/newsreader/fixtures/local/fixture.json index ffcc4fd..99176b5 100644 --- a/src/newsreader/fixtures/local/fixture.json +++ b/src/newsreader/fixtures/local/fixture.json @@ -47,7 +47,7 @@ "user" : 2, "succeeded" : true, "modified" : "2019-07-20T11:28:16.473Z", - "last_suceeded" : "2019-07-20T11:28:16.316Z", + "last_run" : "2019-07-20T11:28:16.316Z", "name" : "Hackers News", "website_url" : null, "created" : "2019-07-14T13:08:10.374Z", @@ -65,7 +65,7 @@ "error" : null, "user" : 2, "succeeded" : true, - "last_suceeded" : "2019-07-20T11:28:15.691Z", + "last_run" : "2019-07-20T11:28:15.691Z", "name" : "BBC", "modified" : "2019-07-20T12:07:49.164Z", "timezone" : "UTC", @@ -85,7 +85,7 @@ "website_url" : null, "name" : "Ars Technica", "succeeded" : true, - "last_suceeded" : "2019-07-20T11:28:15.986Z", + "last_run" : "2019-07-20T11:28:15.986Z", "modified" : "2019-07-20T11:28:16.033Z", "user" : 2 }, @@ -102,7 +102,7 @@ "user" : 2, "name" : "The Guardian", "succeeded" : true, - "last_suceeded" : "2019-07-20T11:28:16.078Z", + "last_run" : "2019-07-20T11:28:16.078Z", "modified" : "2019-07-20T12:07:44.292Z", "created" : "2019-07-20T11:25:02.089Z", "website_url" : null, @@ -119,7 +119,7 @@ "website_url" : null, "created" : "2019-07-20T11:25:30.121Z", "user" : 2, - "last_suceeded" : "2019-07-20T11:28:15.860Z", + "last_run" : "2019-07-20T11:28:15.860Z", "succeeded" : true, "modified" : "2019-07-20T12:07:28.473Z", "name" : "Tweakers" @@ -139,7 +139,7 @@ "website_url" : null, "timezone" : "UTC", "user" : 2, - "last_suceeded" : "2019-07-20T11:28:16.034Z", + "last_run" : "2019-07-20T11:28:16.034Z", "succeeded" : true, "modified" : "2019-07-20T12:07:21.704Z", "name" : "The Verge" diff --git a/src/newsreader/js/components/Messages.js b/src/newsreader/js/components/Messages.js index 843677c..dd3b2f8 100644 --- a/src/newsreader/js/components/Messages.js +++ b/src/newsreader/js/components/Messages.js @@ -3,26 +3,24 @@ import React from 'react'; class Messages extends React.Component { state = { messages: this.props.messages }; - close = ::this.close; - - close(index) { + 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) => { return (
  • - {message.text} this.close(index)} /> + {message.text} this.close(index)} />
  • ); }); - return
      {messages}
    ; + return
      {messages}
    ; } } diff --git a/src/newsreader/js/components/NavList.js b/src/newsreader/js/components/NavList.js new file mode 100644 index 0000000..172ddfe --- /dev/null +++ b/src/newsreader/js/components/NavList.js @@ -0,0 +1,22 @@ +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 8b701f5..8933a59 100644 --- a/src/newsreader/js/components/Selector.js +++ b/src/newsreader/js/components/Selector.js @@ -1,6 +1,4 @@ class Selector { - onClick = ::this.onClick; - inputs = []; constructor() { @@ -11,13 +9,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 new file mode 100644 index 0000000..1bf45ab --- /dev/null +++ b/src/newsreader/js/components/Sidebar.js @@ -0,0 +1,25 @@ +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 1ed14ed..0cb4335 100644 --- a/src/newsreader/js/index.js +++ b/src/newsreader/js/index.js @@ -1,3 +1,5 @@ +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/index.js b/src/newsreader/js/lib/index.js new file mode 100644 index 0000000..31b03f6 --- /dev/null +++ b/src/newsreader/js/lib/index.js @@ -0,0 +1 @@ +import './theme.js'; diff --git a/src/newsreader/js/lib/theme.js b/src/newsreader/js/lib/theme.js new file mode 100644 index 0000000..7d89c5f --- /dev/null +++ b/src/newsreader/js/lib/theme.js @@ -0,0 +1,76 @@ +const isCSSVariablesSupported = () => { + return window.CSS && window.CSS.supports('color', 'var(--fake-color'); +}; + +const changeTheme = event => { + const currentPref = sessionStorage.getItem('t-dark'); + const isDark = currentPref && currentPref === 'true' ? true : false; + + if (isDark) { + document.documentElement.classList.remove('dark-theme'); + } else { + document.documentElement.classList.add('dark-theme'); + } + + try { + sessionStorage.setItem('t-dark', !isDark); + } catch (error) { + // do nothing. + } +}; + +const getThemePreference = () => { + try { + const currentPref = sessionStorage.getItem('t-dark'); + + if (currentPref && currentPref === 'true') { + return true; + } else if ( + !currentPref && + window.matchMedia('(prefers-color-scheme: dark)').matches + ) { + return true; + } else { + return false; + } + } catch (error) { + return false; + } +}; + +const toggleDarkTheme = isDark => { + if (isDark) { + document.documentElement.classList.add('dark-theme'); + } else { + document.documentElement.classList.remove('dark-theme'); + } + + try { + sessionStorage.setItem('t-dark', isDark); + } catch (error) { + // do nothing. + } +}; + +const initThemeSelector = () => { + const themeButton = document.getElementsByClassName('theme-switcher')[0]; + const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)'); + + if (getThemePreference()) { + toggleDarkTheme(true); + } + + themeButton.addEventListener('click', changeTheme); + + prefersDarkTheme.addListener(mediaQuery => { + toggleDarkTheme(mediaQuery.matches); + }); +}; + +const init = () => { + if (isCSSVariablesSupported()) { + initThemeSelector(); + } +}; + +init(); diff --git a/src/newsreader/js/pages/categories/App.js b/src/newsreader/js/pages/categories/App.js index 691aaed..db81a73 100644 --- a/src/newsreader/js/pages/categories/App.js +++ b/src/newsreader/js/pages/categories/App.js @@ -6,12 +6,9 @@ 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); @@ -23,15 +20,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', @@ -59,7 +56,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; @@ -69,6 +66,7 @@ class App extends React.Component { key={category.pk} category={category} showDialog={this.selectCategory} + updateUrl={this.props.updateUrl} /> ); }); @@ -80,7 +78,7 @@ class App extends React.Component { const pageHeader = ( <>

    Categories

    - + Create category @@ -89,15 +87,19 @@ class App extends React.Component { return ( <> {this.state.message && } - - {cards} - {selectedCategory && ( - - )} + + +
    + + {cards} + {selectedCategory && ( + + )} +
    ); } diff --git a/src/newsreader/js/pages/categories/components/CategoryCard.js b/src/newsreader/js/pages/categories/components/CategoryCard.js index 94bd6f4..2ba446c 100644 --- a/src/newsreader/js/pages/categories/components/CategoryCard.js +++ b/src/newsreader/js/pages/categories/components/CategoryCard.js @@ -11,7 +11,7 @@ const CategoryCard = props => { if (rule.favicon) { favicon = ; } else { - favicon = ; + favicon = ; } return ( @@ -33,7 +33,7 @@ const CategoryCard = props => { <> Edit diff --git a/src/newsreader/js/pages/categories/index.js b/src/newsreader/js/pages/categories/index.js index 9d75bb9..77d6940 100644 --- a/src/newsreader/js/pages/categories/index.js +++ b/src/newsreader/js/pages/categories/index.js @@ -9,5 +9,19 @@ if (page) { const dataScript = document.getElementById('categories-data'); const categories = JSON.parse(dataScript.textContent); - ReactDOM.render(, 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 new file mode 100644 index 0000000..98083a5 --- /dev/null +++ b/src/newsreader/js/pages/default/index.js @@ -0,0 +1,17 @@ +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 91cfa4e..e840407 100644 --- a/src/newsreader/js/pages/homepage/App.js +++ b/src/newsreader/js/pages/homepage/App.js @@ -4,13 +4,25 @@ import { connect } from 'react-redux'; import { isEqual } from 'lodash'; import { fetchCategories } from './actions/categories'; +import { filterPosts } from './components/postlist/filters.js'; -import Sidebar from './components/sidebar/Sidebar.js'; +import ScrollTop from './components/ScrollTop.js'; +import HomepageSidebar 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(); } @@ -18,20 +30,32 @@ class App extends React.Component { render() { return ( <> - - - - {this.props.error && ( - - )} + + {!isEqual(this.props.post, {}) && ( )} + + + + {this.props.error && ( + + )} ); } @@ -39,9 +63,10 @@ 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; + const ruleId = state.selected.post.rule.id; const rule = state.rules.items[ruleId]; const category = state.categories.items[rule.category]; @@ -51,10 +76,12 @@ const mapStateToProps = state => { error, rule, post: state.selected.post, + selectedType: state.selected.item.type, + postsByType: postsByType, }; } - return { error, post: state.selected.post }; + return { error, post: state.selected.post, postsByType: postsByType }; }; const mapDispatchToProps = dispatch => ({ diff --git a/src/newsreader/js/pages/homepage/actions/posts.js b/src/newsreader/js/pages/homepage/actions/posts.js index b7ad5cb..68ebe32 100644 --- a/src/newsreader/js/pages/homepage/actions/posts.js +++ b/src/newsreader/js/pages/homepage/actions/posts.js @@ -9,31 +9,41 @@ export const RECEIVE_POST = 'RECEIVE_POST'; export const REQUEST_POSTS = 'REQUEST_POSTS'; export const MARK_POST_READ = 'MARK_POST_READ'; +export const MARKING_POST = 'MARKING_POST'; + +export const MARK_POST_SAVED = 'MARK_POST_SAVED'; +export const MARK_POST_UNSAVED = 'MARK_POST_UNSAVED'; + +export const TOGGLING_POST = 'TOGGLING_POST'; +export const TOGGLED_POST = 'TOGGLED_POST'; export const requestPosts = () => ({ type: REQUEST_POSTS }); - +export const receivePost = post => ({ type: RECEIVE_POST, post }); export const receivePosts = (posts, next) => ({ type: RECEIVE_POSTS, posts, next, }); -export const receivePost = post => ({ type: RECEIVE_POST, post }); - export const selectPost = post => ({ type: SELECT_POST, post }); - export const unSelectPost = () => ({ type: UNSELECT_POST }); +export const markingPostRead = () => ({ type: MARKING_POST }); export const postRead = (post, section) => ({ type: MARK_POST_READ, post, section, }); +export const togglingPost = () => ({ type: TOGGLING_POST }); +export const postToggled = post => ({ type: TOGGLED_POST, post }); + export const markPostRead = (post, token) => { return (dispatch, getState) => { const { selected } = getState(); + dispatch(markingPostRead()); + const url = `/api/posts/${post.id}/`; const options = { method: 'PATCH', @@ -59,7 +69,50 @@ export const markPostRead = (post, token) => { }; }; -export const fetchPostsBySection = (section, page = false) => { +export const toggleSaved = (post, token) => { + return (dispatch, getState) => { + dispatch(togglingPost()); + + const url = `/api/posts/${post.id}/`; + const options = { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': token, + }, + body: JSON.stringify({ saved: !post.saved }), + }; + + return fetch(url, options) + .then(response => response.json()) + .then(updatedPost => { + dispatch(receivePost({ ...updatedPost })); + dispatch(postToggled({ ...updatedPost })); + }) + .catch(error => { + dispatch(receivePost({})); + dispatch(handleAPIError(error)); + }); + }; +}; + +export const fetchSavedPosts = (next = false) => { + return dispatch => { + dispatch(requestPosts()); + + const url = next ? next : '/api/posts/?saved=true'; + + return fetch(url) + .then(response => response.json()) + .then(posts => dispatch(receivePosts(posts.results, posts.next))) + .catch(error => { + dispatch(receivePosts([])); + dispatch(handleAPIError(error)); + }); + }; +}; + +export const fetchPostsBySection = (section, next = false) => { return dispatch => { if (section.unread === 0) { return; @@ -71,10 +124,10 @@ export const fetchPostsBySection = (section, page = false) => { switch (section.type) { case RULE_TYPE: - url = page ? page : `/api/rules/${section.id}/posts/?read=false`; + url = next ? next : `/api/rules/${section.id}/posts/`; break; case CATEGORY_TYPE: - url = page ? page : `/api/categories/${section.id}/posts/?read=false`; + url = next ? next : `/api/categories/${section.id}/posts/`; break; } diff --git a/src/newsreader/js/pages/homepage/actions/selected.js b/src/newsreader/js/pages/homepage/actions/selected.js index 189cad6..44fe79d 100644 --- a/src/newsreader/js/pages/homepage/actions/selected.js +++ b/src/newsreader/js/pages/homepage/actions/selected.js @@ -4,6 +4,9 @@ import { receiveRule, requestRule } from './rules.js'; import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js'; export const MARK_SECTION_READ = 'MARK_SECTION_READ'; +export const SELECT_SAVED = 'SELECT_SAVED'; + +export const selectSaved = () => ({ type: SELECT_SAVED }); export const markSectionRead = section => ({ type: MARK_SECTION_READ, diff --git a/src/newsreader/js/pages/homepage/components/PostModal.js b/src/newsreader/js/pages/homepage/components/PostModal.js index 08033bc..e319e10 100644 --- a/src/newsreader/js/pages/homepage/components/PostModal.js +++ b/src/newsreader/js/pages/homepage/components/PostModal.js @@ -2,12 +2,11 @@ import React from 'react'; import { connect } from 'react-redux'; import Cookies from 'js-cookie'; -import { unSelectPost, markPostRead } from '../actions/posts.js'; -import { CATEGORY_TYPE, RULE_TYPE, FEED, SUBREDDIT } from '../constants.js'; +import { unSelectPost, markPostRead, toggleSaved } from '../actions/posts.js'; +import { SAVED_TYPE } from '../constants.js'; import { formatDatetime } from '../../../utils.js'; class PostModal extends React.Component { - modalListener = ::this.modalListener; readTimer = null; componentDidMount() { @@ -15,7 +14,7 @@ class PostModal extends React.Component { const markPostRead = this.props.markPostRead; const token = Cookies.get('csrftoken'); - if (!post.read) { + if (this.props.autoMarking && this.props.selectedType != SAVED_TYPE && !post.read) { this.readTimer = setTimeout(markPostRead, 3000, post, token); } @@ -32,61 +31,98 @@ 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; + const token = Cookies.get('csrftoken'); const publicationDate = formatDatetime(post.publicationDate); const titleClassName = post.read ? 'post__title post__title--read' : 'post__title'; - const ruleUrl = - this.props.rule.type === FEED - ? `/collection/rules/${this.props.rule.id}/` - : `/collection/rules/subreddits/${this.props.rule.id}/`; + 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'; + + let ruleUrl = ''; + + switch (this.props.rule.type) { + default: + ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`; + break; + } return (
    - this.props.unSelectPost()} - > - Close - -
    -

    {`${post.title} `}

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

    {`${post.title} `}

    +
    +
    + {publicationDate} + {post.author && {post.author}} +
    + +
    + {this.props.category && ( + + + {this.props.category.name} + + + )} + + + {this.props.rule.name} + + + + + + this.props.toggleSaved(post, token)} + /> +
    +
    +
    @@ -101,6 +137,11 @@ class PostModal extends React.Component { const mapDispatchToProps = dispatch => ({ unSelectPost: () => dispatch(unSelectPost()), markPostRead: (post, token) => dispatch(markPostRead(post, token)), + toggleSaved: (post, token) => dispatch(toggleSaved(post, token)), }); -export default connect(null, mapDispatchToProps)(PostModal); +const mapStateToProps = state => ({ + isUpdating: state.posts.isUpdating, +}); + +export default connect(mapStateToProps, mapDispatchToProps)(PostModal); diff --git a/src/newsreader/js/pages/homepage/components/ScrollTop.js b/src/newsreader/js/pages/homepage/components/ScrollTop.js new file mode 100644 index 0000000..9d88c39 --- /dev/null +++ b/src/newsreader/js/pages/homepage/components/ScrollTop.js @@ -0,0 +1,52 @@ +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 9b64289..495f87b 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/PostItem.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostItem.js @@ -1,26 +1,27 @@ import React from 'react'; import { connect } from 'react-redux'; +import Cookies from 'js-cookie'; -import { CATEGORY_TYPE, RULE_TYPE, FEED, SUBREDDIT } from '../../constants.js'; -import { selectPost } from '../../actions/posts.js'; +import { CATEGORY_TYPE, SAVED_TYPE } 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, rule: rule.id }; + const post = { ...this.props.post }; + const token = Cookies.get('csrftoken'); const publicationDate = formatDatetime(post.publicationDate); + const titleClassName = post.read ? 'posts__header posts__header--read' : 'posts__header'; + const savedIconClass = post.saved ? 'saved-icon saved-icon--saved' : 'saved-icon'; - const ruleUrl = - rule.type === FEED - ? `/collection/rules/${rule.id}/` - : `/collection/rules/subreddits/${rule.id}/`; + const ruleUrl = `${this.props.feedUrl}/${rule.id}/`; return ( -
  • +
  • {publicationDate} {post.author && `By ${post.author}`} - {this.props.selected.type == CATEGORY_TYPE && ( + {[CATEGORY_TYPE, SAVED_TYPE].includes(this.props.selected.type) && ( {rule.name} @@ -46,8 +47,12 @@ class PostItem extends React.Component { target="_blank" rel="noopener noreferrer" > - + + this.props.toggleSaved(post, token)} + />
  • ); @@ -56,6 +61,7 @@ class PostItem extends React.Component { const mapDispatchToProps = dispatch => ({ selectPost: post => dispatch(selectPost(post)), + toggleSaved: (post, token) => dispatch(toggleSaved(post, token)), }); export default connect(null, mapDispatchToProps)(PostItem); diff --git a/src/newsreader/js/pages/homepage/components/postlist/PostList.js b/src/newsreader/js/pages/homepage/components/postlist/PostList.js index cd57d6d..12e381e 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/PostList.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostList.js @@ -2,50 +2,80 @@ import React from 'react'; import { connect } from 'react-redux'; import { isEqual } from 'lodash'; -import { fetchPostsBySection } from '../../actions/posts.js'; -import { filterPosts } from './filters.js'; +import { fetchPostsBySection, fetchSavedPosts } from '../../actions/posts.js'; +import { SAVED_TYPE } from '../../constants.js'; import LoadingIndicator from '../../../../components/LoadingIndicator.js'; import PostItem from './PostItem.js'; class PostList extends React.Component { - checkScrollHeight = ::this.checkScrollHeight; + lastPostRef = null; + observer = null; - componentDidMount() { - window.addEventListener('scroll', 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); } - componentWillUnmount() { - window.removeEventListener('scroll', this.checkScrollHeight); - } - - checkScrollHeight(e) { - const currentHeight = window.scrollY + window.innerHeight; - const totalHeight = document.body.offsetHeight; - - const currentPercentage = (currentHeight / totalHeight) * 100; - - if (this.props.next && !this.props.lastReached) { - if (currentPercentage > 60 && !this.props.isFetching) { - this.paginate(); - } + componentDidUpdate() { + if (this.lastPostRef.current && !this.props.isFetching) { + this.observer.observe(this.lastPostRef.current); } } + componentWillUnmount() { + this.observer.disconnect(); + } + + handleIntersect = entries => { + entries.every(entry => { + if (entry.isIntersecting) { + this.observer.unobserve(entry.target); + + if (this.props.next && !this.props.lastReached) { + this.paginate(); + } + + return false; + } + }); + }; + paginate() { - this.props.fetchPostsBySection(this.props.selected, this.props.next); + if (this.props.selected.type === SAVED_TYPE) { + this.props.fetchSavedPosts(this.props.next); + } else { + this.props.fetchPostsBySection(this.props.selected, this.props.next); + } } render() { - const postItems = this.props.postsBySection.map((item, index) => { - return ; + 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 ; + } }); if (isEqual(this.props.selected, {})) { return (
    - +

    Select an item to show its unread posts

    @@ -62,7 +92,7 @@ class PostList extends React.Component { ); } else { return ( -
    +
      {postItems}
    {this.props.isFetching && }
    @@ -73,14 +103,16 @@ class PostList extends React.Component { const mapStateToProps = state => ({ isFetching: state.posts.isFetching, - postsBySection: filterPosts(state), next: state.selected.next, lastReached: state.selected.lastReached, selected: state.selected.item, }); const mapDispatchToProps = dispatch => ({ - fetchPostsBySection: (rule, page = false) => dispatch(fetchPostsBySection(rule, page)), + fetchPostsBySection: (rule, next = false) => dispatch(fetchPostsBySection(rule, next)), + fetchSavedPosts: (next = false) => dispatch(fetchSavedPosts(next)), }); -export default connect(mapStateToProps, mapDispatchToProps)(PostList); +export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })( + PostList +); diff --git a/src/newsreader/js/pages/homepage/components/postlist/filters.js b/src/newsreader/js/pages/homepage/components/postlist/filters.js index 59fd665..b3dd16a 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/filters.js +++ b/src/newsreader/js/pages/homepage/components/postlist/filters.js @@ -1,17 +1,13 @@ -import { CATEGORY_TYPE, RULE_TYPE } from '../../constants.js'; +import { CATEGORY_TYPE, RULE_TYPE, SAVED_TYPE } from '../../constants.js'; const isEmpty = (object = {}) => { return Object.keys(object).length === 0; }; export const filterPostsByRule = (rule = {}, posts = []) => { - const filteredPosts = posts.filter(post => { - return post.rule === rule.id; + return posts.filter(post => { + return post.rule.id === rule.id; }); - - const filteredData = filteredPosts.map(post => ({ ...post, rule: { ...rule } })); - - return filteredData.length > 0 ? [...filteredData] : []; }; export const filterPostsByCategory = (category = {}, rules = [], posts = []) => { @@ -19,30 +15,26 @@ export const filterPostsByCategory = (category = {}, rules = [], posts = []) => return rule.category === category.id; }); - const filteredData = filteredRules.map(rule => { - const filteredPosts = posts.filter(post => { - return post.rule === rule.id; - }); + const ruleIds = filteredRules.map(rule => rule.id); - return filteredPosts.map(post => ({ ...post, rule: { ...rule } })); - }); + return [...posts].filter(post => ruleIds.includes(post.rule.id)); +}; - const sortedPosts = [...filteredData.flat()].sort((firstPost, secondPost) => { - return new Date(secondPost.publicationDate) - new Date(firstPost.publicationDate); - }); - - return sortedPosts; +export const filterPostsBySaved = (rules = [], posts = []) => { + return [...posts].filter(post => post.saved); }; export const filterPosts = state => { const posts = Object.values({ ...state.posts.items }); + const rules = Object.values({ ...state.rules.items }); switch (state.selected.item.type) { case CATEGORY_TYPE: - const rules = Object.values({ ...state.rules.items }); return filterPostsByCategory({ ...state.selected.item }, rules, posts); case RULE_TYPE: return filterPostsByRule({ ...state.selected.item }, posts); + case SAVED_TYPE: + return filterPostsBySaved(rules, posts); } return []; diff --git a/src/newsreader/js/pages/homepage/components/sidebar/CategoryItem.js b/src/newsreader/js/pages/homepage/components/sidebar/CategoryItem.js index 563f8ad..9fcbeb7 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'; @@ -24,9 +24,11 @@ class CategoryItem extends React.Component { } render() { - const chevronClass = this.state.open ? 'gg-chevron-down' : 'gg-chevron-right'; + const chevronClass = this.state.open ? 'fas fa-chevron-down' : 'fas fa-chevron-right'; const selected = isSelected(this.props.category, this.props.selected, CATEGORY_TYPE); - const className = selected ? 'category category--selected' : 'category'; + const className = selected + ? 'sidebar__container sidebar__container--selected' + : 'sidebar__container'; const ruleItems = this.props.rules.map(rule => { return ; @@ -35,13 +37,13 @@ class CategoryItem extends React.Component { return (
  • -
    this.toggleRules()}> + this.toggleRules()}> -
    + -
    this.handleSelect()}> - {this.props.category.name} - {this.props.category.unread} +
    this.handleSelect()}> + {this.props.category.name} + {this.props.category.unread}
    diff --git a/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js b/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js index 3d33fc0..3083b96 100644 --- a/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js +++ b/src/newsreader/js/pages/homepage/components/sidebar/ReadButton.js @@ -5,20 +5,18 @@ import Cookies from 'js-cookie'; import { markRead } from '../../actions/selected.js'; class ReadButton extends React.Component { - markSelectedRead = ::this.markSelectedRead; - - 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 df8da94..a9a9110 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 { @@ -24,7 +24,7 @@ class RuleItem extends React.Component { if (this.props.rule.favicon) { favicon = ; } else { - favicon = ; + favicon = ; } return ( diff --git a/src/newsreader/js/pages/homepage/components/sidebar/SavedItem.js b/src/newsreader/js/pages/homepage/components/sidebar/SavedItem.js new file mode 100644 index 0000000..31b865a --- /dev/null +++ b/src/newsreader/js/pages/homepage/components/sidebar/SavedItem.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import { fetchSavedPosts } from '../../actions/posts.js'; +import { selectSaved } from '../../actions/selected.js'; +import { SAVED_TYPE } from '../../constants.js'; + +class SavedItem extends React.Component { + handleSelect() { + this.props.selectSaved(); + this.props.fetchSavedPosts(); + } + + render() { + const className = + this.props.selected.type === SAVED_TYPE + ? 'sidebar__container sidebar__container--selected' + : 'sidebar__container'; + + return ( +
  • +
    + +
    this.handleSelect()}> + Saved posts +
    +
    +
  • + ); + } +} + +const mapDispatchToProps = dispatch => ({ + selectSaved: () => dispatch(selectSaved()), + fetchSavedPosts: () => dispatch(fetchSavedPosts()), +}); + +export default connect(null, mapDispatchToProps)(SavedItem); diff --git a/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js b/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js index 3780afb..4c6cfad 100644 --- a/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js +++ b/src/newsreader/js/pages/homepage/components/sidebar/Sidebar.js @@ -1,17 +1,19 @@ import React from 'react'; import { connect } from 'react-redux'; -import { isEqual } from 'lodash'; + +import Sidebar from '../../../../components/Sidebar.js'; +import LoadingIndicator from '../../../../components/LoadingIndicator.js'; +import { CATEGORY_TYPE, RULE_TYPE } from '../../constants.js'; + +import CategoryItem from './CategoryItem.js'; +import SavedItem from './SavedItem.js'; +import ReadButton from './ReadButton.js'; import { filterCategories, filterRules } from './filters.js'; -import LoadingIndicator from '../../../../components/LoadingIndicator.js'; -import CategoryItem from './CategoryItem.js'; -import ReadButton from './ReadButton.js'; - -// TODO: show empty category message -class Sidebar extends React.Component { +class HomepageSidebar extends React.Component { render() { - const items = this.props.categories.items.map(category => { + const categoryItems = this.props.categories.items.map(category => { const rules = this.props.rules.items.filter(rule => { return rule.category === category.id; }); @@ -26,16 +28,23 @@ class Sidebar extends React.Component { ); }); + const showReadButton = + this.props.selected.item && + [CATEGORY_TYPE, RULE_TYPE].includes(this.props.selected.item.type); + return ( -
    + {(this.props.categories.isFetching || this.props.rules.isFetching) && ( )} -
      {items}
    +
      + + {categoryItems} +
    - {!isEqual(this.props.selected.item, {}) && } -
    + {showReadButton && } + ); } } @@ -46,4 +55,4 @@ const mapStateToProps = state => ({ selected: state.selected, }); -export default connect(mapStateToProps)(Sidebar); +export default connect(mapStateToProps)(HomepageSidebar); diff --git a/src/newsreader/js/pages/homepage/constants.js b/src/newsreader/js/pages/homepage/constants.js index 66b6365..31524f7 100644 --- a/src/newsreader/js/pages/homepage/constants.js +++ b/src/newsreader/js/pages/homepage/constants.js @@ -1,5 +1,5 @@ export const RULE_TYPE = 'RULE'; export const CATEGORY_TYPE = 'CATEGORY'; +export const SAVED_TYPE = 'SAVED'; -export const SUBREDDIT = 'subreddit'; export const FEED = 'feed'; diff --git a/src/newsreader/js/pages/homepage/index.js b/src/newsreader/js/pages/homepage/index.js index c16ed39..9b56ebd 100644 --- a/src/newsreader/js/pages/homepage/index.js +++ b/src/newsreader/js/pages/homepage/index.js @@ -11,10 +11,22 @@ const page = document.getElementById('homepage--page'); if (page) { const store = configureStore(); - ReactDOM.render( + const settings = JSON.parse(document.getElementById('homepageSettings').textContent); + const { feedUrl, categoriesUrl } = settings; + + const navLinks = JSON.parse(document.getElementById('Links').textContent); + + const app = ( - - , - page + + ); + + ReactDOM.render(app, page); } diff --git a/src/newsreader/js/pages/homepage/reducers/categories.js b/src/newsreader/js/pages/homepage/reducers/categories.js index 612b98f..6a1f253 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 category = {}; + let postCategory = {}; switch (action.section.type) { case CATEGORY_TYPE: - category = { ...state.items[action.section.id] }; + postCategory = { ...state.items[action.section.id] }; break; case RULE_TYPE: - category = { ...state.items[action.section.category] }; + postCategory = { ...state.items[action.section.category] }; break; } @@ -54,33 +54,33 @@ export const categories = (state = { ...defaultState }, action) => { ...state, items: { ...state.items, - [category.id]: { ...category, unread: category.unread - 1 }, + [postCategory.id]: { ...postCategory, unread: postCategory.unread - 1 }, }, }; case MARK_SECTION_READ: - category = {}; + let sectionCategory = {}; switch (action.section.type) { case CATEGORY_TYPE: - category = { ...state.items[action.section.id] }; + sectionCategory = { ...state.items[action.section.id] }; return { ...state, items: { ...state.items, - [category.id]: { ...category, unread: 0 }, + [sectionCategory.id]: { ...sectionCategory, unread: 0 }, }, }; case RULE_TYPE: - category = { ...state.items[action.section.category] }; + sectionCategory = { ...state.items[action.section.category] }; return { ...state, items: { ...state.items, - [category.id]: { - ...category, - unread: category.unread - action.section.unread, + [sectionCategory.id]: { + ...sectionCategory, + unread: sectionCategory.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 220c59b..2c1ff86 100644 --- a/src/newsreader/js/pages/homepage/reducers/posts.js +++ b/src/newsreader/js/pages/homepage/reducers/posts.js @@ -1,67 +1,101 @@ -import { isEqual } from 'lodash'; - import { objectsFromArray } from '../../../utils.js'; import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js'; import { - SELECT_POST, + MARKING_POST, + MARK_POST_READ, RECEIVE_POST, RECEIVE_POSTS, REQUEST_POSTS, + 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 defaultState = { items: {}, isFetching: false }; +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 }; 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: { ...state.items, [action.post.id]: { ...action.post } }, + items: newItems, }; case RECEIVE_POSTS: - const receivedItems = objectsFromArray(action.posts, 'id'); + const receivedPosts = objectsFromArray(action.posts, 'id'); + + let mergedPosts = { ...objectsFromArray(state.items, 'id'), ...receivedPosts }; + let sortedPosts = Object.values(mergedPosts).sort(sortOrdering); return { ...state, isFetching: false, - items: { ...state.items, ...receivedItems }, + items: sortedPosts, }; case MARK_SECTION_READ: - const updatedPosts = {}; - let relatedPosts = []; + let posts = []; switch (action.section.type) { case CATEGORY_TYPE: - relatedPosts = Object.values({ ...state.items }).filter(post => { - return post.rule in { ...action.section.rules }; + posts = [...state.items]; + + posts.forEach(post => { + if (!(post.rule.id in { ...action.section.rules })) return; + + post.read = true; }); break; case RULE_TYPE: - relatedPosts = Object.values({ ...state.items }).filter(post => { - return post.rule === action.section.id; + posts = [...state.items]; + + posts.forEach(post => { + if (post.rule.id != action.section.id) return; + + post.read = true; }); break; } - relatedPosts.forEach(post => { - updatedPosts[post.id] = { ...post, read: true }; - }); - return { ...state, - items: { - ...state.items, - ...updatedPosts, - }, + items: posts, }; + case MARKING_POST: + return { ...state, isUpdating: true }; + case TOGGLING_POST: + return { ...state, isUpdating: true }; + case MARK_POST_READ: + return { ...state, isUpdating: false }; + case TOGGLED_POST: + return { ...state, isUpdating: false }; default: return state; } diff --git a/src/newsreader/js/pages/homepage/reducers/selected.js b/src/newsreader/js/pages/homepage/reducers/selected.js index babcb82..b1f1f98 100644 --- a/src/newsreader/js/pages/homepage/reducers/selected.js +++ b/src/newsreader/js/pages/homepage/reducers/selected.js @@ -9,8 +9,9 @@ import { UNSELECT_POST, } from '../actions/posts.js'; -import { MARK_SECTION_READ } from '../actions/selected.js'; +import { MARK_SECTION_READ, SELECT_SAVED } from '../actions/selected.js'; import { MARK_POST_READ } from '../actions/posts.js'; +import { SAVED_TYPE } from '../constants.js'; const defaultState = { item: {}, next: false, lastReached: false, post: {} }; @@ -47,6 +48,13 @@ export const selected = (state = { ...defaultState }, action) => { next: false, lastReached: false, }; + case SELECT_SAVED: + return { + ...state, + item: { type: SAVED_TYPE }, + next: false, + lastReached: false, + }; case RECEIVE_POSTS: return { ...state, diff --git a/src/newsreader/js/tests/homepage/actions/post.test.js b/src/newsreader/js/tests/homepage/actions/post.test.js index e8f84de..6fd5d3d 100644 --- a/src/newsreader/js/tests/homepage/actions/post.test.js +++ b/src/newsreader/js/tests/homepage/actions/post.test.js @@ -14,12 +14,24 @@ describe('post actions', () => { fetchMock.restore(); }); - it('should create an action request posts', () => { + it('should create an action to request posts', () => { const expectedAction = { type: actions.REQUEST_POSTS }; expect(actions.requestPosts()).toEqual(expectedAction); }); + it('should create an action to mark a post read', () => { + const expectedAction = { type: actions.MARKING_POST }; + + expect(actions.markingPostRead()).toEqual(expectedAction); + }); + + it('should create an action to toggle post saved state', () => { + const expectedAction = { type: actions.TOGGLING_POST }; + + expect(actions.togglingPost()).toEqual(expectedAction); + }); + it('should create an action receive a post', () => { const post = { id: 2067, @@ -33,6 +45,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const expectedAction = { @@ -56,6 +69,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const expectedAction = { @@ -85,6 +99,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const rule = { @@ -105,6 +120,30 @@ describe('post actions', () => { expect(actions.postRead(post, rule)).toEqual(expectedAction); }); + it('should create an action toggling post saved', () => { + const post = { + id: 2067, + remoteIdentifier: 'https://arstechnica.com/?p=1648607', + title: + 'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge', + body: + '"Stale-reference manipulation," 300-character file names, and a clash between worlds.', + author: 'Kyle Orland', + publicationDate: '2020-01-24T19:50:12Z', + url: 'https://arstechnica.com/?p=1648607', + rule: 5, + read: false, + saved: false, + }; + + const expectedAction = { + type: actions.TOGGLED_POST, + post, + }; + + expect(actions.postToggled(post)).toEqual(expectedAction); + }); + it('should create multiple actions to mark post read', () => { const post = { id: 2067, @@ -118,6 +157,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const rule = { @@ -137,7 +177,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: rule, next: false, @@ -147,6 +187,7 @@ describe('post actions', () => { }); const expectedActions = [ + { type: actions.MARKING_POST }, { type: actions.RECEIVE_POST, post: { ...post, read: true }, @@ -163,6 +204,65 @@ describe('post actions', () => { }); }); + it('should create multiple actions to toggle a post saved', () => { + const post = { + id: 2067, + remoteIdentifier: 'https://arstechnica.com/?p=1648607', + title: + 'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge', + body: + '"Stale-reference manipulation," 300-character file names, and a clash between worlds.', + author: 'Kyle Orland', + publicationDate: '2020-01-24T19:50:12Z', + url: 'https://arstechnica.com/?p=1648607', + rule: 5, + read: false, + saved: false, + }; + + const rule = { + id: 1, + name: 'Test rule', + unread: 100, + category: 1, + url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml', + favicon: 'https://cdn.arstechnica.net/favicon.ico', + }; + + fetchMock.patchOnce('/api/posts/2067/', { + body: { ...post, saved: true }, + headers: { 'content-type': 'application/json' }, + }); + + const store = mockStore({ + categories: { items: {}, isFetching: false }, + rules: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, + selected: { + item: rule, + next: false, + lastReached: false, + post: {}, + }, + }); + + const expectedActions = [ + { type: actions.TOGGLING_POST }, + { + type: actions.RECEIVE_POST, + post: { ...post, saved: true }, + }, + { + type: actions.TOGGLED_POST, + post: { ...post, saved: true }, + }, + ]; + + return store.dispatch(actions.toggleSaved(post, 'TOKEN')).then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); + }); + it('should create multiple actions to fetch posts by rule', () => { const posts = [ { @@ -177,6 +277,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }, { id: 2141, @@ -189,6 +290,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648757', rule: 4, read: false, + saved: false, }, ]; @@ -202,10 +304,10 @@ describe('post actions', () => { type: constants.RULE_TYPE, }; - fetchMock.getOnce('/api/rules/4/posts/?read=false', { + fetchMock.getOnce('/api/rules/4/posts/', { body: { count: 2, - next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', + next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar', previous: null, results: posts, }, @@ -215,7 +317,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: {}, next: false, lastReached: false, post: {} }, }); @@ -223,7 +325,7 @@ describe('post actions', () => { { type: actions.REQUEST_POSTS }, { type: actions.RECEIVE_POSTS, - next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', + next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar', posts, }, ]; @@ -247,6 +349,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }, { id: 2141, @@ -259,6 +362,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648757', rule: 4, read: false, + saved: false, }, ]; @@ -269,10 +373,10 @@ describe('post actions', () => { type: constants.CATEGORY_TYPE, }; - fetchMock.getOnce('/api/categories/1/posts/?read=false', { + fetchMock.getOnce('/api/categories/1/posts/', { body: { count: 2, - next: 'https://durp.com/api/categories/4/posts/?page=2&read=false', + next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar', previous: null, results: posts, }, @@ -282,7 +386,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: {}, next: false, lastReached: false, post: {} }, }); @@ -290,7 +394,7 @@ describe('post actions', () => { { type: actions.REQUEST_POSTS }, { type: actions.RECEIVE_POSTS, - next: 'https://durp.com/api/categories/4/posts/?page=2&read=false', + next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar', posts, }, ]; @@ -300,6 +404,67 @@ describe('post actions', () => { }); }); + it('should create multiple actions to fetch posts by saved state', () => { + const posts = [ + { + id: 2067, + remoteIdentifier: 'https://arstechnica.com/?p=1648607', + title: + 'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge', + body: + '"Stale-reference manipulation," 300-character file names, and a clash between worlds.', + author: 'Kyle Orland', + publicationDate: '2020-01-24T19:50:12Z', + url: 'https://arstechnica.com/?p=1648607', + rule: 4, + read: false, + saved: true, + }, + { + id: 2141, + remoteIdentifier: 'https://arstechnica.com/?p=1648757', + title: 'The most complete brain map ever is here: A fly’s “connectome”', + body: + 'It took 12 years and at least $40 million to chart a region about 250µm across.', + author: 'WIRED', + publicationDate: '2020-01-25T11:06:46Z', + url: 'https://arstechnica.com/?p=1648757', + rule: 4, + read: false, + saved: true, + }, + ]; + + fetchMock.getOnce('/api/posts/?saved=true', { + body: { + next: 'https://durp.com/api/posts/?cursor=jabadabar&saved=true', + previous: null, + results: posts, + }, + headers: { 'content-type': 'application/json' }, + }); + + const store = mockStore({ + categories: { items: {}, isFetching: false }, + rules: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, + selected: { item: {}, next: false, lastReached: false, post: {} }, + }); + + const expectedActions = [ + { type: actions.REQUEST_POSTS }, + { + type: actions.RECEIVE_POSTS, + next: 'https://durp.com/api/posts/?cursor=jabadabar&saved=true', + posts, + }, + ]; + + return store.dispatch(actions.fetchSavedPosts()).then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); + }); + it('should create no actions when fetching posts and section is read', () => { const rule = { id: 4, @@ -313,7 +478,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: {}, next: false, lastReached: false, post: {} }, }); @@ -337,6 +502,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const rule = { @@ -357,11 +523,12 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: { ...rule }, next: false, lastReached: false, post: {} }, }); const expectedActions = [ + { type: actions.MARKING_POST }, { type: actions.RECEIVE_POST, post: {} }, { type: errorActions.RECEIVE_API_ERROR, error: TypeError(errorMessage) }, ]; @@ -371,6 +538,55 @@ describe('post actions', () => { }); }); + it('should handle exceptions when toggling a post saved/unsaved', () => { + const post = { + id: 2067, + remoteIdentifier: 'https://arstechnica.com/?p=1648607', + title: + 'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge', + body: + '"Stale-reference manipulation," 300-character file names, and a clash between worlds.', + author: 'Kyle Orland', + publicationDate: '2020-01-24T19:50:12Z', + url: 'https://arstechnica.com/?p=1648607', + rule: 5, + read: false, + saved: false, + }; + + const rule = { + id: 4, + name: 'Ars Technica', + unread: 100, + category: 1, + url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml', + favicon: 'https://cdn.arstechnica.net/favicon.ico', + }; + + const errorMessage = 'Permission denied'; + + fetchMock.patch(`/api/posts/${post.id}/`, () => { + throw new Error(errorMessage); + }); + + const store = mockStore({ + categories: { items: {}, isFetching: false }, + rules: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, + selected: { item: { ...rule }, next: false, lastReached: false, post: {} }, + }); + + const expectedActions = [ + { type: actions.TOGGLING_POST }, + { type: actions.RECEIVE_POST, post: {} }, + { type: errorActions.RECEIVE_API_ERROR, error: Error(errorMessage) }, + ]; + + return store.dispatch(actions.toggleSaved(post, 'FAKE_TOKEN')).then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); + }); + it('should handle exceptions when fetching posts by section', () => { const rule = { id: 4, @@ -384,14 +600,14 @@ describe('post actions', () => { const errorMessage = 'Page not found'; - fetchMock.getOnce(`/api/rules/${rule.id}/posts/?read=false`, () => { + fetchMock.getOnce(`/api/rules/${rule.id}/posts/`, () => { throw new Error(errorMessage); }); const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: { ...rule }, next: false, lastReached: false, post: {} }, }); diff --git a/src/newsreader/js/tests/homepage/actions/selected.test.js b/src/newsreader/js/tests/homepage/actions/selected.test.js index b0f163c..cac7509 100644 --- a/src/newsreader/js/tests/homepage/actions/selected.test.js +++ b/src/newsreader/js/tests/homepage/actions/selected.test.js @@ -32,6 +32,14 @@ describe('selected actions', () => { expect(actions.markSectionRead(category)).toEqual(expectedAction); }); + it('should create an action to select saved items', () => { + const expectedAction = { + type: actions.SELECT_SAVED, + }; + + expect(actions.selectSaved()).toEqual(expectedAction); + }); + it('should mark a category as read', () => { const category = { id: 1, name: 'Test category', unread: 100 }; const rules = { diff --git a/src/newsreader/js/tests/homepage/reducers/post.test.js b/src/newsreader/js/tests/homepage/reducers/post.test.js index ef4234a..59f0e36 100644 --- a/src/newsreader/js/tests/homepage/reducers/post.test.js +++ b/src/newsreader/js/tests/homepage/reducers/post.test.js @@ -6,13 +6,13 @@ 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', () => { const action = { type: actions.REQUEST_POSTS }; - const expectedState = { ...defaultState, isFetching: true }; + const expectedState = { ...defaultState, isFetching: true, isUpdating: false }; expect(reducer(undefined, action)).toEqual(expectedState); }); @@ -30,6 +30,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }; const action = { @@ -40,7 +41,8 @@ describe('post actions', () => { const expectedState = { ...defaultState, isFetching: false, - items: { [post.id]: post }, + isUpdating: false, + items: [post], }; expect(reducer(undefined, action)).toEqual(expectedState); @@ -60,6 +62,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }, { id: 2141, @@ -72,20 +75,21 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648757', rule: 4, read: false, + saved: false, }, ]; const action = { type: actions.RECEIVE_POSTS, - next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', + next: 'https://durp.com/api/rules/4/posts/?page=2', posts, }; - const expectedPosts = objectsFromArray(posts, 'id'); const expectedState = { ...defaultState, isFetching: false, - items: expectedPosts, + isUpdating: false, + items: posts, }; expect(reducer(undefined, action)).toEqual(expectedState); @@ -103,7 +107,14 @@ describe('post actions', () => { author: 'Kyle Orland', publicationDate: '2020-01-24T19:50:12Z', url: 'https://arstechnica.com/?p=1648607', - rule: 5, + 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, + }, read: false, }, 2141: { @@ -115,7 +126,14 @@ describe('post actions', () => { author: 'WIRED', publicationDate: '2020-01-25T11:06:46Z', url: 'https://arstechnica.com/?p=1648757', - rule: 5, + 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, + }, read: false, }, 4637: { @@ -127,8 +145,11 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T19:08:25Z', url: 'https://www.bbc.co.uk/news/world-asia-china-51299195', - rule: 4, + rule: { + id: 4, + }, read: false, + saved: false, }, 4638: { id: 4638, @@ -139,8 +160,11 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T18:27:56Z', url: 'https://www.bbc.co.uk/news/world-europe-51294305', - rule: 4, + rule: { + id: 4, + }, read: false, + saved: false, }, }; @@ -158,16 +182,17 @@ describe('post actions', () => { section: { ...rule, type: constants.RULE_TYPE }, }; - const state = { ...defaultState, items: { ...posts } }; + const state = { ...defaultState, items: Object.values(posts) }; const expectedState = { ...defaultState, isFetching: false, - items: { - ...posts, - 2067: { ...posts[2067], read: true }, - 2141: { ...posts[2141], read: true }, - }, + items: [ + { ...posts[2067], read: true }, + { ...posts[2141], read: true }, + { ...posts[4637] }, + { ...posts[4638] }, + ], }; expect(reducer(state, action)).toEqual(expectedState); @@ -185,8 +210,17 @@ describe('post actions', () => { author: 'Kyle Orland', publicationDate: '2020-01-24T19:50:12Z', url: 'https://arstechnica.com/?p=1648607', - rule: 5, + 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, + }, read: false, + saved: false, }, 2141: { id: 2141, @@ -197,8 +231,17 @@ describe('post actions', () => { author: 'WIRED', publicationDate: '2020-01-25T11:06:46Z', url: 'https://arstechnica.com/?p=1648757', - rule: 5, + 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, + }, read: false, + saved: false, }, 4637: { id: 4637, @@ -209,8 +252,17 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T19:08:25Z', url: 'https://www.bbc.co.uk/news/world-asia-china-51299195', - rule: 4, + 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, + }, read: false, + saved: false, }, 4638: { id: 4638, @@ -221,8 +273,17 @@ describe('post actions', () => { author: null, publicationDate: '2020-01-29T18:27:56Z', url: 'https://www.bbc.co.uk/news/world-europe-51294305', - rule: 4, + 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, + }, read: false, + saved: false, }, 4589: { id: 4589, @@ -234,8 +295,11 @@ 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: 7, + rule: { + id: 7, + }, read: false, + saved: false, }, 4594: { id: 4594, @@ -247,8 +311,11 @@ describe('post actions', () => { publicationDate: '2020-01-29T16:29:40Z', url: 'https://tweakers.net/nieuws/162870/samsung-kondigt-eerste-tablet-met-5g-aan.html', - rule: 7, + rule: { + id: 7, + }, read: false, + saved: false, }, }; @@ -263,7 +330,7 @@ describe('post actions', () => { unread: 321, }, 5: { - id: 4, + id: 5, name: 'BBC', url: 'http://feeds.bbci.co.uk/news/world/rss.xml', favicon: @@ -288,18 +355,19 @@ describe('post actions', () => { }, }; - const state = { ...defaultState, items: { ...posts } }; + const state = { ...defaultState, items: Object.values(posts) }; const expectedState = { ...defaultState, isFetching: false, - items: { - ...posts, - 2067: { ...posts[2067], read: true }, - 2141: { ...posts[2141], read: true }, - 4637: { ...posts[4637], read: true }, - 4638: { ...posts[4638], read: true }, - }, + items: [ + { ...posts[2067], read: true }, + { ...posts[2141], read: true }, + { ...posts[4589] }, + { ...posts[4594] }, + { ...posts[4637], read: true }, + { ...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 215c6e1..0d1a7ae 100644 --- a/src/newsreader/js/tests/homepage/reducers/selected.test.js +++ b/src/newsreader/js/tests/homepage/reducers/selected.test.js @@ -52,6 +52,19 @@ describe('selected reducer', () => { expect(reducer(undefined, action)).toEqual(expectedState); }); + it('should return state after selecting saved items', () => { + const action = { + type: actions.SELECT_SAVED, + }; + + const expectedState = { + ...defaultState, + item: { type: constants.SAVED_TYPE }, + }; + + expect(reducer(undefined, action)).toEqual(expectedState); + }); + it('should return state after selecting a category twice', () => { const category = { id: 9, name: 'Tech', unread: 291 }; @@ -241,13 +254,13 @@ describe('selected reducer', () => { const action = { type: postActions.RECEIVE_POSTS, - next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', + next: 'https://durp.com/api/rules/4/posts/?page=2', posts, }; const expectedState = { ...defaultState, - next: 'https://durp.com/api/rules/4/posts/?page=2&read=false', + next: 'https://durp.com/api/rules/4/posts/?page=2', lastReached: false, }; diff --git a/src/newsreader/news/apps.py b/src/newsreader/news/apps.py index 42c63ba..d29f1a6 100644 --- a/src/newsreader/news/apps.py +++ b/src/newsreader/news/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class NewsConfig(AppConfig): - name = "news" + name = "newsreader.news" diff --git a/src/newsreader/news/collection/admin.py b/src/newsreader/news/collection/admin.py index c5a7c5c..ece5c23 100644 --- a/src/newsreader/news/collection/admin.py +++ b/src/newsreader/news/collection/admin.py @@ -6,14 +6,7 @@ from newsreader.news.collection.models import CollectionRule class CollectionRuleAdmin(admin.ModelAdmin): fields = ("url", "name", "timezone", "category", "favicon", "user") - list_display = ( - "name", - "type_display", - "category", - "url", - "last_suceeded", - "succeeded", - ) + list_display = ("name", "type_display", "category", "url", "last_run", "succeeded") list_filter = ("user",) def save_model(self, request, obj, form, change): diff --git a/src/newsreader/news/collection/apps.py b/src/newsreader/news/collection/apps.py index 1f4c1c0..a836fea 100644 --- a/src/newsreader/news/collection/apps.py +++ b/src/newsreader/news/collection/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class CollectionConfig(AppConfig): - name = "collection" + name = "newsreader.news.collection" diff --git a/src/newsreader/news/collection/base.py b/src/newsreader/news/collection/base.py index f980191..7286526 100644 --- a/src/newsreader/news/collection/base.py +++ b/src/newsreader/news/collection/base.py @@ -1,7 +1,10 @@ -from bs4 import BeautifulSoup +import bleach -from newsreader.news.collection.exceptions import StreamParseException -from newsreader.news.collection.utils import fetch +from newsreader.news.collection.constants import ( + WHITELISTED_ATTRIBUTES, + WHITELISTED_TAGS, +) +from newsreader.news.core.models import Post class Stream: @@ -20,19 +23,16 @@ class Stream: def parse(self, response): raise NotImplementedError - class Meta: - abstract = True - class Client: """ - Retrieves the data with streams + Retrieves the data through streams """ stream = Stream def __init__(self, rules=[]): - self.rules = rules if rules else CollectionRule.objects.enabled() + self.rules = rules def __enter__(self): for rule in self.rules: @@ -43,36 +43,40 @@ class Client: def __exit__(self, *args, **kwargs): pass - class Meta: - abstract = True - class Builder: """ - Creates the collected posts + Builds instances of various types """ instances = [] stream = None + payload = None - def __init__(self, stream): + def __init__(self, payload, stream): + self.payload = payload self.stream = stream def __enter__(self): - self.create_posts(self.stream) return self def __exit__(self, *args, **kwargs): pass - def create_posts(self, stream): - pass + def build(self): + raise NotImplementedError - def save(self): - pass + def sanitize_fragment(self, fragment): + if not fragment: + return "" - class Meta: - abstract = True + return bleach.clean( + fragment, + tags=WHITELISTED_TAGS, + attributes=WHITELISTED_ATTRIBUTES, + strip=True, + strip_comments=True, + ) class Collector: @@ -88,46 +92,54 @@ class Collector: self.builder = builder if builder else self.builder def collect(self, rules=None): - with self.client(rules=rules) as client: - for data, stream in client: - with self.builder((data, stream)) as builder: - builder.save() - - class Meta: - abstract = True + raise NotImplementedError -class WebsiteStream(Stream): - def __init__(self, url): - self.url = url +class Scheduler: + """ + Schedules rules according to certain ratelimitting + """ - def read(self): - response = fetch(self.url) - - return (self.parse(response.content), self) - - def parse(self, payload): - try: - return BeautifulSoup(payload, "lxml") - except TypeError: - raise StreamParseException("Could not parse given HTML") + def get_scheduled_rules(self): + raise NotImplementedError -class URLBuilder(Builder): +class PostBuilder(Builder): + rule_type = None + def __enter__(self): - return self + self.existing_posts = { + post.remote_identifier: post + for post in Post.objects.filter( + rule=self.stream.rule, rule__type=self.rule_type + ) + } - def build(self): - data, stream = self.stream - rule = stream.rule + return super().__enter__() - try: - url = data["feed"]["link"] - except (KeyError, TypeError): - url = None + def save(self): + for post in self.instances: + post.save() - if url: - rule.website_url = url - rule.save() - return rule, url +class PostStream(Stream): + rule_type = None + + +class PostClient(Client): + stream = PostStream + + def set_rule_error(self, rule, exception): + length = rule._meta.get_field("error").max_length + + rule.error = exception.message[-length:] + rule.succeeded = False + + +class PostCollector(Collector): + def collect(self, rules=[]): + with self.client(rules=rules) as client: + for payload, stream in client: + with self.builder(payload, stream) as builder: + builder.build() + builder.save() diff --git a/src/newsreader/news/collection/choices.py b/src/newsreader/news/collection/choices.py index 65f7ef5..05b6eb3 100644 --- a/src/newsreader/news/collection/choices.py +++ b/src/newsreader/news/collection/choices.py @@ -4,4 +4,3 @@ from django.utils.translation import gettext as _ class RuleTypeChoices(TextChoices): feed = "feed", _("Feed") - subreddit = "subreddit", _("Subreddit") diff --git a/src/newsreader/news/collection/constants.py b/src/newsreader/news/collection/constants.py index eade898..0c73642 100644 --- a/src/newsreader/news/collection/constants.py +++ b/src/newsreader/news/collection/constants.py @@ -23,6 +23,7 @@ WHITELISTED_TAGS = ( WHITELISTED_ATTRIBUTES = { **BLEACH_ATTRIBUTES, "a": ["href", "rel"], - "img": ["alt", "src"], - "source": ["srcset", "media", "src", "type"], + "img": ["alt", "src", "loading"], + "video": ["controls", "muted"], + "source": ["srcset", "src", "media", "type"], } diff --git a/src/newsreader/news/collection/endpoints.py b/src/newsreader/news/collection/endpoints.py index 7f2ede0..c5c5580 100644 --- a/src/newsreader/news/collection/endpoints.py +++ b/src/newsreader/news/collection/endpoints.py @@ -1,41 +1,30 @@ +from django.db.models import Prefetch + from rest_framework import status from rest_framework.generics import ( GenericAPIView, ListAPIView, - RetrieveUpdateDestroyAPIView, + RetrieveUpdateAPIView, get_object_or_404, ) from rest_framework.response import Response -from newsreader.core.pagination import LargeResultSetPagination, ResultSetPagination +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 ReadFilter from newsreader.news.core.models import Post from newsreader.news.core.serializers import PostSerializer -class ListRuleView(ListAPIView): +class DetailRuleView(RetrieveUpdateAPIView): queryset = CollectionRule.objects.all() serializer_class = RuleSerializer - pagination_class = ResultSetPagination - - def get_queryset(self): - user = self.request.user - return self.queryset.filter(user=user).order_by("-created") - - -class DetailRuleView(RetrieveUpdateDestroyAPIView): - queryset = CollectionRule.objects.all() - serializer_class = RuleSerializer - pagination_class = ResultSetPagination class NestedRuleView(ListAPIView): queryset = CollectionRule.objects.prefetch_related("posts").all() serializer_class = PostSerializer - pagination_class = LargeResultSetPagination - filter_backends = [ReadFilter] + pagination_class = CursorPagination def get_queryset(self): lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field @@ -44,7 +33,9 @@ class NestedRuleView(ListAPIView): # filtered on the user. filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} - rule = get_object_or_404(self.queryset, **filter_kwargs) + prefetch = Prefetch("posts", queryset=Post.objects.filter(read=False)) + queryset = CollectionRule.objects.prefetch_related(prefetch) + rule = get_object_or_404(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 new file mode 100644 index 0000000..5d02b32 --- /dev/null +++ b/src/newsreader/news/collection/exceptions/__init__.py @@ -0,0 +1,34 @@ +from newsreader.news.collection.exceptions.builder import ( + BuilderDuplicateException, + BuilderException, + BuilderMissingDataException, + BuilderParseException, + BuilderSkippedException, +) +from newsreader.news.collection.exceptions.stream import ( + StreamConnectionException, + StreamDeniedException, + StreamException, + StreamForbiddenException, + StreamNotFoundException, + StreamParseException, + StreamTimeOutException, + StreamTooManyException, +) + + +__all__ = [ + "BuilderDuplicateException", + "BuilderException", + "BuilderMissingDataException", + "BuilderParseException", + "BuilderSkippedException", + "StreamConnectionException", + "StreamDeniedException", + "StreamException", + "StreamForbiddenException", + "StreamNotFoundException", + "StreamParseException", + "StreamTimeOutException", + "StreamTooManyException", +] diff --git a/src/newsreader/news/collection/exceptions/builder.py b/src/newsreader/news/collection/exceptions/builder.py new file mode 100644 index 0000000..0c2f520 --- /dev/null +++ b/src/newsreader/news/collection/exceptions/builder.py @@ -0,0 +1,25 @@ +class BuilderException(Exception): + message = "Builder exception" + + def __init__(self, payload=None, message=None): + self.payload = payload + self.message = message if message else self.message + + def __str__(self): + return self.message + + +class BuilderMissingDataException(BuilderException): + message = "Payload contains missing data" + + +class BuilderDuplicateException(BuilderException): + message = "Payload contains duplicate entry" + + +class BuilderParseException(BuilderException): + message = "Failed to parse payload" + + +class BuilderSkippedException(BuilderException): + message = "Payload does not meet filter criteria" diff --git a/src/newsreader/news/collection/exceptions.py b/src/newsreader/news/collection/exceptions/stream.py similarity index 100% rename from src/newsreader/news/collection/exceptions.py rename to src/newsreader/news/collection/exceptions/stream.py diff --git a/src/newsreader/news/collection/favicon.py b/src/newsreader/news/collection/favicon.py index 44b96bf..17d4045 100644 --- a/src/newsreader/news/collection/favicon.py +++ b/src/newsreader/news/collection/favicon.py @@ -1,16 +1,12 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from urllib.parse import urljoin, urlparse -from newsreader.news.collection.base import ( - Builder, - Client, - Collector, - Stream, - URLBuilder, - WebsiteStream, -) -from newsreader.news.collection.exceptions import StreamException +from bs4 import BeautifulSoup + +from newsreader.news.collection.base import Builder, Client, Collector, Stream +from newsreader.news.collection.exceptions import StreamException, StreamParseException from newsreader.news.collection.feed import FeedClient +from newsreader.news.collection.utils import fetch LINK_RELS = [ @@ -21,17 +17,45 @@ LINK_RELS = [ ] +class WebsiteStream(Stream): + def read(self): + response = fetch(self.rule.website_url) + + return self.parse(response.content), self + + def parse(self, payload): + try: + return BeautifulSoup(payload, features="lxml") + except TypeError: + raise StreamParseException("Could not parse given HTML") + + +class WebsiteURLBuilder(Builder): + def build(self): + try: + url = self.payload["feed"]["link"] + except (KeyError, TypeError): + url = None + + self.instances = [(self.stream, url)] if url else [] + + def save(self): + for stream, url in self.instances: + stream.rule.website_url = url + stream.rule.save() + + class FaviconBuilder(Builder): def build(self): - rule, soup = self.stream + rule = self.stream.rule - url = self.parse(soup, rule.website_url) + url = self.parse() - if url: - rule.favicon = url - rule.save() + self.instances = [(rule, url)] if url else [] + + def parse(self): + soup = self.payload - def parse(self, soup, website_url): if not soup.head: return @@ -44,9 +68,9 @@ class FaviconBuilder(Builder): parsed_url = urlparse(url) if not parsed_url.scheme and not parsed_url.netloc: - if not website_url: + if not self.stream.rule.website_url: return - return urljoin(website_url, url) + return urljoin(self.stream.rule.website_url, url) elif not parsed_url.scheme: return urljoin(f"https://{parsed_url.netloc}", parsed_url.path) @@ -57,7 +81,7 @@ class FaviconBuilder(Builder): icons = set() for link in links: - if not "href" in link.attrs: + if "href" not in link.attrs: continue if "favicon" in link["href"]: @@ -73,6 +97,11 @@ class FaviconBuilder(Builder): elif icons: return icons.pop() + def save(self): + for rule, favicon_url in self.instances: + rule.favicon = favicon_url + rule.save() + class FaviconClient(Client): stream = WebsiteStream @@ -82,39 +111,35 @@ class FaviconClient(Client): def __enter__(self): with ThreadPoolExecutor(max_workers=10) as executor: - futures = { - executor.submit(stream.read): rule for rule, stream in self.streams - } + futures = [executor.submit(stream.read) for stream in self.streams] for future in as_completed(futures): - rule = futures[future] - try: - response_data, stream = future.result() + payload, stream = future.result() except StreamException: continue - yield (rule, response_data) + yield payload, stream class FaviconCollector(Collector): feed_client, favicon_client = (FeedClient, FaviconClient) - url_builder, favicon_builder = (URLBuilder, FaviconBuilder) + url_builder, favicon_builder = (WebsiteURLBuilder, FaviconBuilder) - def collect(self, rules=None): + def collect(self, rules=[]): streams = [] with self.feed_client(rules=rules) as client: - for data, stream in client: - with self.url_builder((data, stream)) as builder: - rule, url = builder.build() + for payload, stream in client: + with self.url_builder(payload, stream) as builder: + builder.build() + builder.save() - if not url: - continue - - streams.append((rule, WebsiteStream(url))) + if builder.instances: + streams.append(WebsiteStream(stream.rule)) with self.favicon_client(streams) as client: - for rule, data in client: - with self.favicon_builder((rule, data)) as builder: + for payload, stream in client: + with self.favicon_builder(payload, stream) as builder: builder.build() + builder.save() diff --git a/src/newsreader/news/collection/feed.py b/src/newsreader/news/collection/feed.py index f67a109..87f7d85 100644 --- a/src/newsreader/news/collection/feed.py +++ b/src/newsreader/news/collection/feed.py @@ -6,25 +6,21 @@ from datetime import timedelta from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.utils import timezone -import bleach -import pytz - from feedparser import parse -from newsreader.news.collection.base import Builder, Client, Collector, Stream -from newsreader.news.collection.choices import RuleTypeChoices -from newsreader.news.collection.constants import ( - WHITELISTED_ATTRIBUTES, - WHITELISTED_TAGS, +from newsreader.news.collection.base import ( + PostBuilder, + PostClient, + PostCollector, + PostStream, ) +from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.exceptions import ( - StreamDeniedException, StreamException, StreamNotFoundException, StreamParseException, StreamTimeOutException, ) -from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.utils import ( build_publication_date, fetch, @@ -36,32 +32,22 @@ from newsreader.news.core.models import Post logger = logging.getLogger(__name__) -class FeedBuilder(Builder): - instances = [] +class FeedBuilder(PostBuilder): + rule__type = RuleTypeChoices.feed - def __enter__(self): - _, stream = self.stream + def build(self): + instances = [] - self.instances = [] - self.existing_posts = { - post.remote_identifier: post - for post in Post.objects.filter( - rule=stream.rule, rule__type=RuleTypeChoices.feed - ) - } + with FeedDuplicateHandler(self.stream.rule) as duplicate_handler: + entries = self.payload.get("entries", []) - return super().__enter__() + for entry in entries: + post = self.build_post(entry) + instances.append(post) - def create_posts(self, stream): - data, stream = stream - - with FeedDuplicateHandler(stream.rule) as duplicate_handler: - entries = data.get("entries", []) - - instances = self.build(entries, stream.rule) self.instances = duplicate_handler.check(instances) - def build(self, entries, rule): + def build_post(self, entry): field_mapping = { "id": "remote_identifier", "title": "title", @@ -70,56 +56,45 @@ class FeedBuilder(Builder): "published_parsed": "publication_date", "author": "author", } + data = {"rule_id": self.stream.rule.pk} - tz = pytz.timezone(rule.timezone) + for field, model_field in field_mapping.items(): + if field not in entry: + continue - for entry in entries: - data = {"rule_id": rule.pk} + value = truncate_text(Post, model_field, entry[field]) - for field, model_field in field_mapping.items(): - if not field in entry: - continue + if field == "published_parsed": + data[model_field] = build_publication_date(value) + elif field == "summary": + data[model_field] = self.sanitize_fragment(value) + else: + data[model_field] = value - value = truncate_text(Post, model_field, entry[field]) + content_details = self.get_content_details(entry) - if field == "published_parsed": - data[model_field] = build_publication_date(value, tz) - elif field == "summary": - data[model_field] = self.sanitize_fragment(value) - else: - data[model_field] = value + # use content details key if it contains more information + if "body" not in data or len(data["body"]) < len(content_details): + data["body"] = content_details - if "content" in entry: - content = self.get_content(entry["content"]) - body = data.get("body", "") + return Post(**data) - if not body or len(body) < len(content): - data["body"] = content + def get_content_details(self, entry): + content_items = entry.get("content") - yield Post(**data) - - def sanitize_fragment(self, fragment): - if not fragment: + if not content_items: return "" - return bleach.clean( - fragment, - tags=WHITELISTED_TAGS, - attributes=WHITELISTED_ATTRIBUTES, - strip=True, - strip_comments=True, - ) + content_details = "\n ".join([item.get("value") for item in content_items]) + return self.sanitize_fragment(content_details) - def get_content(self, items): - content = "\n ".join([item.get("value") for item in items]) - return self.sanitize_fragment(content) - - def save(self): - for post in self.instances: - post.save() + def __str__(self): + return f"{self.stream.rule.pk}: FeedBuilder" -class FeedStream(Stream): +class FeedStream(PostStream): + rule_type = RuleTypeChoices.feed + def read(self): response = fetch(self.rule.url) @@ -132,18 +107,13 @@ class FeedStream(Stream): message = "Could not parse feed" raise StreamParseException(response=response, message=message) from e + def __str__(self): + return f"{self.rule.pk}: FeedStream" -class FeedClient(Client): + +class FeedClient(PostClient): stream = FeedStream - def __init__(self, rules=[]): - if rules: - self.rules = rules - else: - self.rules = CollectionRule.objects.filter( - enabled=True, type=RuleTypeChoices.feed - ) - def __enter__(self): streams = [self.stream(rule) for rule in self.rules] @@ -154,13 +124,12 @@ class FeedClient(Client): stream = futures[future] try: - response_data = future.result() + payload = future.result() stream.rule.error = None stream.rule.succeeded = True - stream.rule.last_suceeded = timezone.now() - yield response_data + yield payload except (StreamNotFoundException, StreamTimeOutException) as e: logger.warning(f"Request failed for {stream.rule.url}") @@ -174,16 +143,11 @@ class FeedClient(Client): continue finally: + stream.rule.last_run = timezone.now() stream.rule.save() - def set_rule_error(self, rule, exception): - length = rule._meta.get_field("error").max_length - rule.error = exception.message[-length:] - rule.succeeded = False - - -class FeedCollector(Collector): +class FeedCollector(PostCollector): builder = FeedBuilder client = FeedClient diff --git a/src/newsreader/news/collection/forms.py b/src/newsreader/news/collection/forms.py deleted file mode 100644 index c79a867..0000000 --- a/src/newsreader/news/collection/forms.py +++ /dev/null @@ -1,101 +0,0 @@ -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.core.forms import CheckboxInput -from newsreader.news.collection.choices import RuleTypeChoices -from newsreader.news.collection.models import CollectionRule -from newsreader.news.collection.reddit import REDDIT_API_URL -from newsreader.news.core.models import Category - - -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 CollectionRuleForm(forms.ModelForm): - category = forms.ModelChoiceField(required=False, queryset=Category.objects.all()) - 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, - ) - - def __init__(self, *args, **kwargs): - self.user = kwargs.pop("user") - - super().__init__(*args, **kwargs) - - self.fields["category"].queryset = Category.objects.filter(user=self.user) - - def save(self, commit=True): - instance = super().save(commit=False) - instance.user = self.user - - if commit: - instance.save() - self.save_m2m() - - return instance - - class Meta: - model = CollectionRule - fields = ("name", "url", "timezone", "favicon", "category") - - -class CollectionRuleBulkForm(forms.Form): - rules = forms.ModelMultipleChoiceField(queryset=CollectionRule.objects.none()) - - def __init__(self, user, *args, **kwargs): - self.user = user - - super().__init__(*args, **kwargs) - - self.fields["rules"].queryset = CollectionRule.objects.filter(user=user) - - -class SubRedditRuleForm(CollectionRuleForm): - url = forms.URLField(max_length=1024, help_text=get_reddit_help_text) - - timezone = None - - 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") - - -class OPMLImportForm(forms.Form): - file = forms.FileField(allow_empty_file=False) - skip_existing = forms.BooleanField( - initial=False, required=False, widget=CheckboxInput - ) diff --git a/src/newsreader/news/collection/forms/__init__.py b/src/newsreader/news/collection/forms/__init__.py new file mode 100644 index 0000000..d5ee1bd --- /dev/null +++ b/src/newsreader/news/collection/forms/__init__.py @@ -0,0 +1,9 @@ +from newsreader.news.collection.forms.feed import FeedForm, OPMLImportForm +from newsreader.news.collection.forms.rules import CollectionRuleBulkForm + + +__all__ = [ + "FeedForm", + "OPMLImportForm", + "CollectionRuleBulkForm", +] diff --git a/src/newsreader/news/collection/forms/base.py b/src/newsreader/news/collection/forms/base.py new file mode 100644 index 0000000..da23659 --- /dev/null +++ b/src/newsreader/news/collection/forms/base.py @@ -0,0 +1,29 @@ +from django import forms + +from newsreader.news.collection.models import CollectionRule +from newsreader.news.core.models import Category + + +class CollectionRuleForm(forms.ModelForm): + category = forms.ModelChoiceField(required=False, queryset=Category.objects.all()) + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop("user") + + super().__init__(*args, **kwargs) + + self.fields["category"].queryset = Category.objects.filter(user=self.user) + + def save(self, commit=True): + instance = super().save(commit=False) + instance.user = self.user + + if commit: + instance.save() + self.save_m2m() + + return instance + + class Meta: + model = CollectionRule + fields = "__all__" diff --git a/src/newsreader/news/collection/forms/feed.py b/src/newsreader/news/collection/forms/feed.py new file mode 100644 index 0000000..0b52c5f --- /dev/null +++ b/src/newsreader/news/collection/forms/feed.py @@ -0,0 +1,18 @@ +from django import forms + +from newsreader.core.forms import CheckboxInput +from newsreader.news.collection.forms.base import CollectionRuleForm +from newsreader.news.collection.models import CollectionRule + + +class FeedForm(CollectionRuleForm): + class Meta: + model = CollectionRule + fields = ("name", "url", "favicon", "category") + + +class OPMLImportForm(forms.Form): + file = forms.FileField(allow_empty_file=False) + skip_existing = forms.BooleanField( + initial=False, required=False, widget=CheckboxInput + ) diff --git a/src/newsreader/news/collection/forms/rules.py b/src/newsreader/news/collection/forms/rules.py new file mode 100644 index 0000000..fade945 --- /dev/null +++ b/src/newsreader/news/collection/forms/rules.py @@ -0,0 +1,14 @@ +from django import forms + +from newsreader.news.collection.models import CollectionRule + + +class CollectionRuleBulkForm(forms.Form): + rules = forms.ModelMultipleChoiceField(queryset=CollectionRule.objects.none()) + + def __init__(self, user, *args, **kwargs): + self.user = user + + super().__init__(*args, **kwargs) + + self.fields["rules"].queryset = CollectionRule.objects.filter(user=user) diff --git a/src/newsreader/news/collection/management/commands/collect.py b/src/newsreader/news/collection/management/commands/collect.py deleted file mode 100644 index 7d928f0..0000000 --- a/src/newsreader/news/collection/management/commands/collect.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.core.management.base import BaseCommand - -from newsreader.news.collection.feed import FeedCollector - - -class Command(BaseCommand): - help = "Collects Atom/RSS feeds" - - def handle(self, *args, **options): - collector = FeedCollector() - collector.collect() diff --git a/src/newsreader/news/collection/management/commands/fetch_favicons.py b/src/newsreader/news/collection/management/commands/fetch_favicons.py deleted file mode 100644 index 1ee96cf..0000000 --- a/src/newsreader/news/collection/management/commands/fetch_favicons.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.core.management.base import BaseCommand - -from newsreader.news.collection.favicon import FaviconCollector - - -class Command(BaseCommand): - help = "Fetch favicons for collection rules" - - def handle(self, *args, **options): - collector = FaviconCollector() - collector.collect() diff --git a/src/newsreader/news/collection/migrations/0001_initial.py b/src/newsreader/news/collection/migrations/0001_initial.py index 59910e5..d53ff0d 100644 --- a/src/newsreader/news/collection/migrations/0001_initial.py +++ b/src/newsreader/news/collection/migrations/0001_initial.py @@ -6,7 +6,6 @@ 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 6854c0b..32a9ea6 100644 --- a/src/newsreader/news/collection/migrations/0002_auto_20190714_1036.py +++ b/src/newsreader/news/collection/migrations/0002_auto_20190714_1036.py @@ -7,7 +7,6 @@ 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 99f1018..945d94d 100644 --- a/src/newsreader/news/collection/migrations/0003_auto_20190714_1417.py +++ b/src/newsreader/news/collection/migrations/0003_auto_20190714_1417.py @@ -5,7 +5,6 @@ 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 4e9efb2..f2a30b8 100644 --- a/src/newsreader/news/collection/migrations/0004_auto_20190714_1422.py +++ b/src/newsreader/news/collection/migrations/0004_auto_20190714_1422.py @@ -6,7 +6,6 @@ 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 cdd3e32..9d2f615 100644 --- a/src/newsreader/news/collection/migrations/0005_auto_20200303_1932.py +++ b/src/newsreader/news/collection/migrations/0005_auto_20200303_1932.py @@ -4,7 +4,6 @@ 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 441d7f1..6745b65 100644 --- a/src/newsreader/news/collection/migrations/0006_auto_20200412_1955.py +++ b/src/newsreader/news/collection/migrations/0006_auto_20200412_1955.py @@ -7,7 +7,6 @@ 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 fe6b0eb..a282a42 100644 --- a/src/newsreader/news/collection/migrations/0007_collectionrule_enabled.py +++ b/src/newsreader/news/collection/migrations/0007_collectionrule_enabled.py @@ -4,7 +4,6 @@ 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 bb8975d..946296c 100644 --- a/src/newsreader/news/collection/migrations/0008_collectionrule_type.py +++ b/src/newsreader/news/collection/migrations/0008_collectionrule_type.py @@ -4,7 +4,6 @@ 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 new file mode 100644 index 0000000..980e0cf --- /dev/null +++ b/src/newsreader/news/collection/migrations/0009_auto_20200807_2030.py @@ -0,0 +1,28 @@ +# Generated by Django 3.0.7 on 2020-08-07 18:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("collection", "0008_collectionrule_type")] + + operations = [ + migrations.AddField( + model_name="collectionrule", + name="screen_name", + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AlterField( + model_name="collectionrule", + name="type", + field=models.CharField( + choices=[ + ("feed", "Feed"), + ("subreddit", "Subreddit"), + ("twitter", "Twitter"), + ], + default="feed", + max_length=20, + ), + ), + ] diff --git a/src/newsreader/news/collection/migrations/0010_auto_20200913_2101.py b/src/newsreader/news/collection/migrations/0010_auto_20200913_2101.py new file mode 100644 index 0000000..68428ca --- /dev/null +++ b/src/newsreader/news/collection/migrations/0010_auto_20200913_2101.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.7 on 2020-09-13 19:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("collection", "0009_auto_20200807_2030")] + + operations = [ + migrations.AlterField( + model_name="collectionrule", + name="type", + field=models.CharField( + choices=[ + ("feed", "Feed"), + ("subreddit", "Subreddit"), + ("twitter_timeline", "Twitter timeline"), + ], + default="feed", + max_length=20, + ), + ) + ] diff --git a/src/newsreader/news/collection/migrations/0011_auto_20200913_2157.py b/src/newsreader/news/collection/migrations/0011_auto_20200913_2157.py new file mode 100644 index 0000000..75d5bf5 --- /dev/null +++ b/src/newsreader/news/collection/migrations/0011_auto_20200913_2157.py @@ -0,0 +1,13 @@ +# Generated by Django 3.0.7 on 2020-09-13 19:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [("collection", "0010_auto_20200913_2101")] + + operations = [ + migrations.RenameField( + model_name="collectionrule", old_name="last_suceeded", new_name="last_run" + ) + ] diff --git a/src/newsreader/news/collection/migrations/0012_auto_20201219_1331.py b/src/newsreader/news/collection/migrations/0012_auto_20201219_1331.py new file mode 100644 index 0000000..078bb7c --- /dev/null +++ b/src/newsreader/news/collection/migrations/0012_auto_20201219_1331.py @@ -0,0 +1,48 @@ +# Generated by Django 3.0.7 on 2020-12-19 12:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("collection", "0011_auto_20200913_2157")] + + operations = [ + migrations.AddField( + model_name="collectionrule", + name="reddit_allow_nfsw", + field=models.BooleanField(default=False, verbose_name="Allow NSFW posts"), + ), + migrations.AddField( + model_name="collectionrule", + name="reddit_allow_spoiler", + field=models.BooleanField(default=False, verbose_name="Allow spoilers"), + ), + migrations.AddField( + model_name="collectionrule", + name="reddit_allow_viewed", + field=models.BooleanField( + default=True, verbose_name="Allow already seen posts" + ), + ), + migrations.AddField( + model_name="collectionrule", + name="reddit_comments_min", + field=models.PositiveIntegerField( + default=0, verbose_name="Minimum amount of comments" + ), + ), + migrations.AddField( + model_name="collectionrule", + name="reddit_downvotes_max", + field=models.PositiveIntegerField( + default=0, verbose_name="Maximum amount of downvotes" + ), + ), + migrations.AddField( + model_name="collectionrule", + name="reddit_upvotes_min", + field=models.PositiveIntegerField( + default=0, verbose_name="Minimum amount of upvotes" + ), + ), + ] diff --git a/src/newsreader/news/collection/migrations/0013_auto_20201219_1345.py b/src/newsreader/news/collection/migrations/0013_auto_20201219_1345.py new file mode 100644 index 0000000..05698ad --- /dev/null +++ b/src/newsreader/news/collection/migrations/0013_auto_20201219_1345.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.7 on 2020-12-19 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("collection", "0012_auto_20201219_1331")] + + operations = [ + migrations.AlterField( + model_name="collectionrule", + name="reddit_downvotes_max", + field=models.PositiveIntegerField( + blank=True, null=True, verbose_name="Maximum amount of downvotes" + ), + ) + ] diff --git a/src/newsreader/news/collection/migrations/0014_auto_20201219_1346.py b/src/newsreader/news/collection/migrations/0014_auto_20201219_1346.py new file mode 100644 index 0000000..337f715 --- /dev/null +++ b/src/newsreader/news/collection/migrations/0014_auto_20201219_1346.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.7 on 2020-12-19 12:46 + +from django.db import migrations + + +def reset_default_downvotes(apps, schema_editor): + CollectionRule = apps.get_model("collection", "CollectionRule") + + for rule in CollectionRule.objects.all(): + rule.reddit_downvotes_max = None + rule.save() + + +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 new file mode 100644 index 0000000..6838bb6 --- /dev/null +++ b/src/newsreader/news/collection/migrations/0015_alter_collectionrule_timezone.py @@ -0,0 +1,625 @@ +# Generated by Django 3.2 on 2021-04-23 20:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("collection", "0014_auto_20201219_1346")] + + 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/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/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/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/0016_alter_collectionrule_timezone.py b/src/newsreader/news/collection/migrations/0016_alter_collectionrule_timezone.py new file mode 100644 index 0000000..768d315 --- /dev/null +++ b/src/newsreader/news/collection/migrations/0016_alter_collectionrule_timezone.py @@ -0,0 +1,630 @@ +# 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 new file mode 100644 index 0000000..db72ecc --- /dev/null +++ b/src/newsreader/news/collection/migrations/0017_remove_collectionrule_timezone.py @@ -0,0 +1,16 @@ +# 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 new file mode 100644 index 0000000..cc61aee --- /dev/null +++ b/src/newsreader/news/collection/migrations/0018_remove_collectionrule_reddit_allow_nfsw_and_more.py @@ -0,0 +1,47 @@ +# 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 35841ba..e665b16 100644 --- a/src/newsreader/news/collection/models.py +++ b/src/newsreader/news/collection/models.py @@ -2,8 +2,6 @@ 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 @@ -25,12 +23,6 @@ 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, @@ -41,9 +33,8 @@ class CollectionRule(TimeStampedModel): on_delete=models.SET_NULL, ) - last_suceeded = models.DateTimeField(blank=True, null=True) + last_run = models.DateTimeField(blank=True, null=True) succeeded = models.BooleanField(default=False) - error = models.CharField(max_length=1024, blank=True, null=True) enabled = models.BooleanField( @@ -64,7 +55,12 @@ class CollectionRule(TimeStampedModel): @property def update_url(self): - if self.type == RuleTypeChoices.subreddit: - return reverse("news:collection:subreddit-update", kwargs={"pk": self.pk}) + return reverse("news:collection:feed-update", kwargs={"pk": self.pk}) - return reverse("news:collection:rule-update", kwargs={"pk": self.pk}) + @property + def source_url(self): + return self.url + + @property + def failed(self): + return not self.succeeded and self.last_run diff --git a/src/newsreader/news/collection/reddit.py b/src/newsreader/news/collection/reddit.py deleted file mode 100644 index 557271c..0000000 --- a/src/newsreader/news/collection/reddit.py +++ /dev/null @@ -1,362 +0,0 @@ -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 bleach -import pytz -import requests - -from newsreader.news.collection.base import Builder, Client, Collector, Stream -from newsreader.news.collection.choices import RuleTypeChoices -from newsreader.news.collection.constants import ( - WHITELISTED_ATTRIBUTES, - WHITELISTED_TAGS, -) -from newsreader.news.collection.exceptions import ( - 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"] - - -class RedditBuilder(Builder): - def __enter__(self): - _, stream = self.stream - - self.instances = [] - self.existing_posts = { - post.remote_identifier: post - for post in Post.objects.filter( - rule=stream.rule, rule__type=RuleTypeChoices.subreddit - ) - } - - return super().__enter__() - - def create_posts(self, stream): - data, stream = stream - posts = [] - - if not "data" in data or not "children" in data["data"]: - return - - posts = data["data"]["children"] - self.instances = self.build(posts, stream.rule) - - def build(self, posts, rule): - results = {} - - for post in posts: - if not "data" in post or post["kind"] != REDDIT_POST: - continue - - data = post["data"] - - remote_identifier = data["id"] - title = truncate_text(Post, "title", data["title"]) - author = truncate_text(Post, "author", data["author"]) - post_url_fragment = data["permalink"] - direct_url = data["url"] - is_text_post = data["is_self"] - - if remote_identifier in results: - continue - - if is_text_post: - uncleaned_body = data["selftext_html"] - unescaped_body = unescape(uncleaned_body) if uncleaned_body else "" - body = ( - bleach.clean( - unescaped_body, - tags=WHITELISTED_TAGS, - attributes=WHITELISTED_ATTRIBUTES, - strip=True, - strip_comments=True, - ) - if unescaped_body - else "" - ) - elif direct_url.endswith(REDDIT_IMAGE_EXTENSIONS): - body = format_html( - "
    {title}
    ", - url=direct_url, - title=title, - ) - elif data["is_video"]: - video_info = data["secure_media"]["reddit_video"] - - body = format_html( - "
    ", - url=video_info["fallback_url"], - ) - elif direct_url.endswith(REDDIT_VIDEO_EXTENSIONS): - extension = next( - extension.replace(".", "") - for extension in REDDIT_VIDEO_EXTENSIONS - if direct_url.endswith(extension) - ) - - if extension == "gifv": - body = format_html( - "
    ", - url=direct_url.replace(extension, "mp4"), - ) - else: - body = format_html( - "
    ", - url=direct_url, - extension=extension, - ) - else: - body = format_html( - "", - url=direct_url, - title=title, - ) - - try: - parsed_date = datetime.fromtimestamp(post["data"]["created_utc"]) - created_date = pytz.utc.localize(parsed_date) - except (OverflowError, OSError): - logging.warning(f"Failed parsing timestamp from {url_fragment}") - created_date = timezone.now() - - post_data = { - "remote_identifier": remote_identifier, - "title": title, - "body": body, - "author": author, - "url": f"{REDDIT_URL}{post_url_fragment}", - "publication_date": created_date, - "rule": rule, - } - - if remote_identifier in self.existing_posts: - existing_post = self.existing_posts[remote_identifier] - - for key, value in post_data.items(): - setattr(existing_post, key, value) - - results[existing_post.remote_identifier] = existing_post - continue - - results[remote_identifier] = Post(**post_data) - - return results.values() - - def save(self): - for post in self.instances: - post.save() - - -class RedditScheduler: - 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_suceeded")[: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()) - - -class RedditStream(Stream): - headers = {} - user = None - - def __init__(self, rule): - super().__init__(rule) - - self.user = self.rule.user - 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=f"Failed parsing json" - ) from e - - -class RedditClient(Client): - stream = RedditStream - - def __init__(self, rules=[]): - self.rules = rules - - 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: - break - - for future in as_completed(futures): - stream = futures[future] - - try: - response_data = future.result() - - stream.rule.error = None - stream.rule.succeeded = True - stream.rule.last_suceeded = timezone.now() - - yield response_data - except StreamDeniedException as e: - logger.warning( - f"Access token expired for user {stream.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( - "Stream failed reading content from " f"{stream.rule.url}" - ) - - self.set_rule_error(stream.rule, e) - - continue - finally: - stream.rule.save() - - def set_rule_error(self, rule, exception): - length = rule._meta.get_field("error").max_length - - rule.error = exception.message[-length:] - rule.succeeded = False - - -class RedditCollector(Collector): - builder = RedditBuilder - client = RedditClient diff --git a/src/newsreader/news/collection/response_handler.py b/src/newsreader/news/collection/response_handler.py index 2cd785d..1af6ddb 100644 --- a/src/newsreader/news/collection/response_handler.py +++ b/src/newsreader/news/collection/response_handler.py @@ -42,7 +42,9 @@ class ResponseHandler: stream_exception = StreamException message = getattr(exception, "message", str(exception)) - raise stream_exception(exception.response, message=message) from exception + raise stream_exception( + response=exception.response, message=message + ) from exception def __exit__(self, *args, **kwargs): pass diff --git a/src/newsreader/news/collection/tasks.py b/src/newsreader/news/collection/tasks.py index d368a5c..b61936a 100644 --- a/src/newsreader/news/collection/tasks.py +++ b/src/newsreader/news/collection/tasks.py @@ -1,15 +1,12 @@ -from django.conf import settings from django.core.exceptions import ObjectDoesNotExist -import requests - from celery.exceptions import Reject from celery.utils.log import get_task_logger 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 @@ -33,7 +30,7 @@ class FeedTask(app.Task): if acquired: logger.info(f"Running task for user {user_pk}") - rules = user.rules.enabled() + rules = user.rules.enabled().filter(type=RuleTypeChoices.feed) collector = FeedCollector() collector.collect(rules=rules) @@ -45,34 +42,12 @@ 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" +class FaviconTask(app.Task): + name = "FaviconTask" ignore_result = True def run(self, user_pk): - from newsreader.news.collection.reddit import REDDIT_URL + from newsreader.news.collection.favicon import FaviconCollector try: user = User.objects.get(pk=user_pk) @@ -82,37 +57,19 @@ class RedditTokenTask(app.Task): 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) + with MemCacheLock("f{user.email}-favicon-task", self.app.oid) as acquired: + if acquired: + logger.info(f"Running favicon task for user {user_pk}") - client_auth = requests.auth.HTTPBasicAuth( - settings.REDDIT_CLIENT_ID, settings.REDDIT_CLIENT_SECRET - ) + rules = user.rules.enabled().filter(type=RuleTypeChoices.feed) - 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}" - ) + collector = FaviconCollector() + collector.collect(rules=rules) + else: + logger.warning("Cancelling task due to existing lock") - user.reddit_refresh_token = None - user.save() - return - - response_data = response.json() - - user.reddit_access_token = response_data["access_token"] - user.save() + raise Reject(reason="Task already running", requeue=False) FeedTask = app.register_task(FeedTask()) -RedditTask = app.register_task(RedditTask()) -RedditTokenTask = app.register_task(RedditTokenTask()) +FaviconTask = app.register_task(FaviconTask()) 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 new file mode 100644 index 0000000..ed5aef8 --- /dev/null +++ b/src/newsreader/news/collection/templates/news/collection/views/feed-create.html @@ -0,0 +1,13 @@ +{% extends "sidebar.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" %} +
    +
    +{% 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 new file mode 100644 index 0000000..ea614ef --- /dev/null +++ b/src/newsreader/news/collection/templates/news/collection/views/feed-update.html @@ -0,0 +1,17 @@ +{% extends "sidebar.html" %} +{% load static i18n %} + +{% block content %} + {% url "news:collection:rules" as cancel_url %} + {% trans "Failed to retrieve posts" as title %} + +
    +
    + {% 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" %} +
    +
    +{% 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 df19887..b93894c 100644 --- a/src/newsreader/news/collection/templates/news/collection/views/import.html +++ b/src/newsreader/news/collection/templates/news/collection/views/import.html @@ -1,9 +1,13 @@ -{% extends "base.html" %} +{% extends "sidebar.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 rules" %} + {% 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/rule-create.html b/src/newsreader/news/collection/templates/news/collection/views/rule-create.html deleted file mode 100644 index 82ed6c5..0000000 --- a/src/newsreader/news/collection/templates/news/collection/views/rule-create.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} -{% load static %} - -{% block content %} -
    - {% url "news:collection:rules" as cancel_url %} - {% include "components/form/form.html" with form=form title="Create rule" cancel_url=cancel_url confirm_text="Create rule" %} -
    -{% endblock %} diff --git a/src/newsreader/news/collection/templates/news/collection/views/rule-update.html b/src/newsreader/news/collection/templates/news/collection/views/rule-update.html deleted file mode 100644 index 0a705b8..0000000 --- a/src/newsreader/news/collection/templates/news/collection/views/rule-update.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "base.html" %} -{% load static i18n %} - -{% block content %} -
    - {% if rule.error %} - {% trans "Failed to retrieve posts" as title %} - {% include "components/textbox/textbox.html" with title=title body=rule.error class="text-section--error" only %} - {% endif %} - - {% url "news:collection:rules" as cancel_url %} - {% include "components/form/form.html" with form=form title="Update rule" cancel_url=cancel_url confirm_text="Save rule" only %} -
    -{% 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 0cd1870..77c2034 100644 --- a/src/newsreader/news/collection/templates/news/collection/views/rules.html +++ b/src/newsreader/news/collection/templates/news/collection/views/rules.html @@ -1,25 +1,32 @@ -{% extends "base.html" %} +{% extends "sidebar.html" %} {% load i18n static filters %} {% block content %} -
    +
    +
    {% csrf_token %}
    -
    - - - -
    -
    +
    +
    + + + +
    +
    +
    @@ -27,47 +34,69 @@ - - - - - + + + + + - + {% for rule in rules %} - - - - - - - - + + + + + + + + + + + + + {% endfor %}
    {% 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" %} +
    - {% 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.url }} - - {% if rule.succeeded %} - - {% 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 %} +
    @@ -78,24 +107,32 @@
    -
    +
    +
    {% 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 deleted file mode 100644 index 6250e4e..0000000 --- a/src/newsreader/news/collection/templates/news/collection/views/subreddit-create.html +++ /dev/null @@ -1,9 +0,0 @@ -{% 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 deleted file mode 100644 index 0099e3b..0000000 --- a/src/newsreader/news/collection/templates/news/collection/views/subreddit-update.html +++ /dev/null @@ -1,14 +0,0 @@ -{% 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/tests/endpoints/rule/detail/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py index 8dfe6ed..5c6b1e0 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"], "Not found.") + self.assertEquals(data["detail"], "No CollectionRule matches the given query.") def test_post(self): rule = FeedFactory(user=self.user) @@ -64,22 +64,6 @@ 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) @@ -121,15 +105,6 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(response.status_code, 200) self.assertEquals(data["name"], "BBC") - def test_delete(self): - rule = FeedFactory(user=self.user) - - response = self.client.delete( - reverse("api:news:collection:rules-detail", args=[rule.pk]) - ) - - self.assertEquals(response.status_code, 204) - def test_rule_with_unauthenticated_user(self): self.client.logout() 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 4d1ba8f..24d4b9a 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py @@ -1,148 +1,15 @@ import json -from datetime import date, datetime, time +from datetime import datetime, timezone 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 -class RuleListViewTestCase(TestCase): - def setUp(self): - self.user = UserFactory(password="test") - self.client.force_login(self.user) - - def test_simple(self): - FeedFactory.create_batch(size=3, user=self.user) - - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - def test_ordering(self): - rules = [ - FeedFactory( - created=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), - user=self.user, - ), - FeedFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc - ), - user=self.user, - ), - FeedFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc - ), - user=self.user, - ), - ] - - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - self.assertEquals(data["results"][0]["id"], rules[1].pk) - self.assertEquals(data["results"][1]["id"], rules[2].pk) - self.assertEquals(data["results"][2]["id"], rules[0].pk) - - def test_pagination_count(self): - FeedFactory.create_batch(size=80, user=self.user) - - response = self.client.get( - reverse("api:news:collection:rules-list"), {"count": 30} - ) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 80) - self.assertEquals(len(data["results"]), 30) - - def test_empty(self): - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) - - def test_post(self): - category = CategoryFactory(user=self.user) - - data = {"name": "BBC", "url": "https://www.bbc.co.uk", "category": category.pk} - - response = self.client.post( - reverse("api:news:collection:rules-list"), - data=json.dumps(data), - content_type="application/json", - ) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') - - def test_patch(self): - response = self.client.patch(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') - - def test_put(self): - response = self.client.put(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') - - def test_delete(self): - response = self.client.delete(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') - - def test_rules_with_unauthenticated_user(self): - self.client.logout() - - FeedFactory.create_batch(size=3, user=self.user) - - response = self.client.get(reverse("api:news:collection:rules-list")) - - self.assertEquals(response.status_code, 403) - - def test_rules_with_unauthorized_user(self): - other_user = UserFactory() - FeedFactory.create_batch(size=3, user=other_user) - - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) - - class NestedRuleListViewTestCase(TestCase): def setUp(self): self.user = UserFactory(password="test") @@ -157,15 +24,19 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 5) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 5) + self.assertEqual(data["next"], None) + self.assertEqual(data["previous"], None) def test_pagination(self): rule = FeedFactory.create(user=self.user) - FeedPostFactory.create_batch(size=80, rule=rule) + + posts = sorted( + FeedPostFactory.create_batch(size=80, rule=rule), + key=lambda post: post.publication_date, + reverse=True, + ) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), @@ -173,9 +44,14 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 80) - self.assertEquals(len(data["results"]), 30) + self.assertEqual(response.status_code, 200) + self.assertTrue(data["next"]) + self.assertFalse(data["previous"]) + + self.assertEqual(len(data["results"]), 30) + self.assertEqual( + [post["id"] for post in data["results"]], [post.id for post in posts[:30]] + ) def test_empty(self): rule = FeedFactory.create(user=self.user) @@ -185,16 +61,15 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 0) def test_not_known(self): response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": 0}) ) - self.assertEquals(response.status_code, 404) + self.assertEqual(response.status_code, 404) def test_post(self): rule = FeedFactory.create(user=self.user) @@ -206,8 +81,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "POST" not allowed.') def test_patch(self): rule = FeedFactory.create(user=self.user) @@ -219,8 +94,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): rule = FeedFactory.create(user=self.user) @@ -232,8 +107,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): rule = FeedFactory.create(user=self.user) @@ -245,8 +120,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_rule_with_unauthenticated_user(self): self.client.logout() @@ -257,7 +132,7 @@ class NestedRuleListViewTestCase(TestCase): reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_rule_with_unauthorized_user(self): other_user = UserFactory() @@ -267,7 +142,7 @@ class NestedRuleListViewTestCase(TestCase): reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_posts_ordering(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -276,22 +151,22 @@ class NestedRuleListViewTestCase(TestCase): FeedPostFactory( title="I'm the first post", rule=rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc + publication_date=( + datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc) ), ), FeedPostFactory( title="I'm the second post", rule=rule, - publication_date=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc + publication_date=( + datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc) ), ), FeedPostFactory( title="I'm the third post", rule=rule, - publication_date=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc + publication_date=( + datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc) ), ), ] @@ -301,14 +176,12 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 3) - self.assertEquals(data["results"][0]["id"], posts[1].pk) - self.assertEquals(data["results"][1]["id"], posts[2].pk) - self.assertEquals(data["results"][2]["id"], posts[0].pk) + self.assertEqual(data["results"][0]["id"], posts[1].pk) + self.assertEqual(data["results"][1]["id"], posts[2].pk) + self.assertEqual(data["results"][2]["id"], posts[0].pk) def test_only_posts_from_rule_are_returned(self): rule = FeedFactory.create(user=self.user) @@ -322,16 +195,14 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 5) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 5) for post in data["results"]: - self.assertEquals(post["rule"], rule.pk) + with self.subTest(post=post): + self.assertEqual(post["rule"]["id"], rule.pk) - def test_unread_posts(self): + def test_posts(self): rule = FeedFactory.create(user=self.user) FeedPostFactory.create_batch(size=10, rule=rule, read=False) @@ -339,34 +210,13 @@ class NestedRuleListViewTestCase(TestCase): response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), - {"read": "false"}, ) data = response.json() - posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) - for post in posts: - self.assertEquals(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() - posts = data["results"] - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) - - for post in posts: - self.assertEquals(post["read"], True) + for post in data["results"]: + with self.subTest(post=post): + self.assertEqual(post["read"], False) diff --git a/src/newsreader/news/collection/tests/factories.py b/src/newsreader/news/collection/tests/factories.py index fdf786f..1f80a98 100644 --- a/src/newsreader/news/collection/tests/factories.py +++ b/src/newsreader/news/collection/tests/factories.py @@ -3,7 +3,6 @@ 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): @@ -23,8 +22,3 @@ class CollectionRuleFactory(factory.django.DjangoModelFactory): class FeedFactory(CollectionRuleFactory): type = RuleTypeChoices.feed - - -class SubredditFactory(CollectionRuleFactory): - type = RuleTypeChoices.subreddit - website_url = REDDIT_URL diff --git a/src/newsreader/news/collection/tests/favicon/builder/tests.py b/src/newsreader/news/collection/tests/favicon/builder/tests.py index e8a1a34..a670f8a 100644 --- a/src/newsreader/news/collection/tests/favicon/builder/tests.py +++ b/src/newsreader/news/collection/tests/favicon/builder/tests.py @@ -1,8 +1,17 @@ +from unittest.mock import Mock + 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 * +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, +) class FaviconBuilderTestCase(TestCase): @@ -12,8 +21,11 @@ class FaviconBuilderTestCase(TestCase): def test_simple(self): rule = CollectionRuleFactory(favicon=None) - with FaviconBuilder((rule, simple_mock)) as builder: + with FaviconBuilder(simple_mock, Mock(rule=rule)) as builder: builder.build() + builder.save() + + rule.refresh_from_db() self.assertEquals(rule.favicon, "https://www.bbc.com/favicon.ico") @@ -22,24 +34,33 @@ class FaviconBuilderTestCase(TestCase): website_url="https://www.theguardian.com/", favicon=None ) - with FaviconBuilder((rule, mock_without_url)) as builder: + with FaviconBuilder(mock_without_url, Mock(rule=rule)) as builder: builder.build() + builder.save() + + rule.refresh_from_db() self.assertEquals(rule.favicon, "https://www.theguardian.com/favicon.ico") def test_without_header(self): rule = CollectionRuleFactory(favicon=None) - with FaviconBuilder((rule, mock_without_header)) as builder: + with FaviconBuilder(mock_without_header, Mock(rule=rule)) as builder: builder.build() + builder.save() + + rule.refresh_from_db() self.assertEquals(rule.favicon, None) def test_weird_path(self): rule = CollectionRuleFactory(favicon=None) - with FaviconBuilder((rule, mock_with_weird_path)) as builder: + with FaviconBuilder(mock_with_weird_path, Mock(rule=rule)) as builder: builder.build() + builder.save() + + rule.refresh_from_db() self.assertEquals( rule.favicon, "https://www.theguardian.com/jabadaba/doe/favicon.ico" @@ -48,15 +69,21 @@ class FaviconBuilderTestCase(TestCase): def test_other_url(self): rule = CollectionRuleFactory(favicon=None) - with FaviconBuilder((rule, mock_with_other_url)) as builder: + with FaviconBuilder(mock_with_other_url, Mock(rule=rule)) as builder: builder.build() + builder.save() + + rule.refresh_from_db() self.assertEquals(rule.favicon, "https://www.theguardian.com/icon.png") def test_url_with_favicon_takes_precedence(self): rule = CollectionRuleFactory(favicon=None) - with FaviconBuilder((rule, mock_with_multiple_icons)) as builder: + with FaviconBuilder(mock_with_multiple_icons, Mock(rule=rule)) as builder: builder.build() + builder.save() + + rule.refresh_from_db() self.assertEquals(rule.favicon, "https://www.bbc.com/favicon.ico") diff --git a/src/newsreader/news/collection/tests/favicon/client/tests.py b/src/newsreader/news/collection/tests/favicon/client/tests.py index 717ee0c..85b8fa3 100644 --- a/src/newsreader/news/collection/tests/favicon/client/tests.py +++ b/src/newsreader/news/collection/tests/favicon/client/tests.py @@ -1,4 +1,4 @@ -from unittest.mock import MagicMock +from unittest.mock import Mock from django.test import TestCase @@ -19,22 +19,22 @@ class FaviconClientTestCase(TestCase): def test_simple(self): rule = CollectionRuleFactory() - stream = MagicMock(url="https://www.bbc.com") + stream = Mock(url="https://www.bbc.com", rule=rule) stream.read.return_value = (simple_mock, stream) - with FaviconClient([(rule, stream)]) as client: - for rule, data in client: - self.assertEquals(rule.pk, rule.pk) - self.assertEquals(data, simple_mock) + with FaviconClient([stream]) as client: + for payload, stream in client: + self.assertEquals(stream.rule.pk, rule.pk) + self.assertEquals(payload, simple_mock) stream.read.assert_called_once_with() def test_client_catches_stream_exception(self): rule = CollectionRuleFactory(error=None, succeeded=True) - stream = MagicMock(url="https://www.bbc.com") + stream = Mock(url="https://www.bbc.com", rule=rule) stream.read.side_effect = StreamException - with FaviconClient([(rule, stream)]) as client: + with FaviconClient([stream]) as client: for rule, data in client: pass @@ -46,10 +46,10 @@ class FaviconClientTestCase(TestCase): def test_client_catches_stream_not_found_exception(self): rule = CollectionRuleFactory(error=None, succeeded=True) - stream = MagicMock(url="https://www.bbc.com") + stream = Mock(url="https://www.bbc.com", rule=rule) stream.read.side_effect = StreamNotFoundException - with FaviconClient([(rule, stream)]) as client: + with FaviconClient([stream]) as client: for rule, data in client: pass @@ -61,10 +61,10 @@ class FaviconClientTestCase(TestCase): def test_client_catches_stream_denied_exception(self): rule = CollectionRuleFactory(error=None, succeeded=True) - stream = MagicMock(url="https://www.bbc.com") + stream = Mock(url="https://www.bbc.com", rule=rule) stream.read.side_effect = StreamDeniedException - with FaviconClient([(rule, stream)]) as client: + with FaviconClient([stream]) as client: for rule, data in client: pass @@ -76,10 +76,10 @@ class FaviconClientTestCase(TestCase): def test_client_catches_stream_timed_out(self): rule = CollectionRuleFactory(error=None, succeeded=True) - stream = MagicMock(url="https://www.bbc.com") + stream = Mock(url="https://www.bbc.com", rule=rule) stream.read.side_effect = StreamTimeOutException - with FaviconClient([(rule, stream)]) as client: + with FaviconClient([stream]) as client: for rule, data in client: pass diff --git a/src/newsreader/news/collection/tests/favicon/collector/mocks.py b/src/newsreader/news/collection/tests/favicon/collector/mocks.py index 3318ffd..d9b65f1 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,7 +134,6 @@ 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 44254a5..23dea56 100644 --- a/src/newsreader/news/collection/tests/favicon/collector/tests.py +++ b/src/newsreader/news/collection/tests/favicon/collector/tests.py @@ -1,4 +1,4 @@ -from unittest.mock import MagicMock, patch +from unittest.mock import Mock, patch from django.test import TestCase @@ -6,7 +6,6 @@ from bs4 import BeautifulSoup from newsreader.news.collection.exceptions import ( StreamDeniedException, - StreamException, StreamForbiddenException, StreamNotFoundException, StreamParseException, @@ -38,8 +37,8 @@ class FaviconCollectorTestCase(TestCase): def test_simple(self): rule = CollectionRuleFactory(succeeded=True, error=None) - self.mocked_feed_client.return_value = [(feed_mock, MagicMock(rule=rule))] - self.mocked_website_read.return_value = (website_mock, MagicMock()) + self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))] + self.mocked_website_read.return_value = (website_mock, Mock(rule=rule)) collector = FaviconCollector() collector.collect() @@ -54,8 +53,11 @@ class FaviconCollectorTestCase(TestCase): def test_empty_stream(self): rule = CollectionRuleFactory(succeeded=True, error=None) - self.mocked_feed_client.return_value = [(feed_mock, MagicMock(rule=rule))] - self.mocked_website_read.return_value = (BeautifulSoup("", "lxml"), MagicMock()) + self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))] + self.mocked_website_read.return_value = ( + BeautifulSoup("", "lxml"), + Mock(rule=rule), + ) collector = FaviconCollector() collector.collect() @@ -70,7 +72,7 @@ class FaviconCollectorTestCase(TestCase): def test_not_found(self): rule = CollectionRuleFactory(succeeded=True, error=None) - self.mocked_feed_client.return_value = [(feed_mock, MagicMock(rule=rule))] + self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))] self.mocked_website_read.side_effect = StreamNotFoundException collector = FaviconCollector() @@ -86,7 +88,7 @@ class FaviconCollectorTestCase(TestCase): def test_denied(self): rule = CollectionRuleFactory(succeeded=True, error=None) - self.mocked_feed_client.return_value = [(feed_mock, MagicMock(rule=rule))] + self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))] self.mocked_website_read.side_effect = StreamDeniedException collector = FaviconCollector() @@ -102,7 +104,7 @@ class FaviconCollectorTestCase(TestCase): def test_forbidden(self): rule = CollectionRuleFactory(succeeded=True, error=None) - self.mocked_feed_client.return_value = [(feed_mock, MagicMock(rule=rule))] + self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))] self.mocked_website_read.side_effect = StreamForbiddenException collector = FaviconCollector() @@ -118,7 +120,7 @@ class FaviconCollectorTestCase(TestCase): def test_timed_out(self): rule = CollectionRuleFactory(succeeded=True, error=None) - self.mocked_feed_client.return_value = [(feed_mock, MagicMock(rule=rule))] + self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))] self.mocked_website_read.side_effect = StreamTimeOutException collector = FaviconCollector() @@ -134,7 +136,7 @@ class FaviconCollectorTestCase(TestCase): def test_wrong_stream_content_type(self): rule = CollectionRuleFactory(succeeded=True, error=None) - self.mocked_feed_client.return_value = [(feed_mock, MagicMock(rule=rule))] + self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))] self.mocked_website_read.side_effect = StreamParseException collector = FaviconCollector() diff --git a/src/newsreader/news/collection/tests/feed/builder/mocks.py b/src/newsreader/news/collection/tests/feed/builder/mocks.py index 83f7d0b..8fedad3 100644 --- a/src/newsreader/news/collection/tests/feed/builder/mocks.py +++ b/src/newsreader/news/collection/tests/feed/builder/mocks.py @@ -108,8 +108,6 @@ 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", @@ -260,6 +258,27 @@ mock_with_long_title = { ] } +mock_with_long_exotic_title = { + "entries": [ + { + "author": "A. Author", + "id": "https://www.bbc.co.uk/news/world-us-canada-48338168", + "link": "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)), + "summary": "Foreign Minister Mohammad Javad Zarif says the US " + "president should try showing Iranians some respect.", + "title": "#ഡെബ്കോണ്‍ഫ്20 ഓണ്‍ലൈന്‍ അവസാന ദിവസം മലയാളം" + "പരിപാടികളോടെയാണു് തുടങ്ങുന്നതു്: നെറ്റ്‌വര്‍ക്ക് വഴി കുറേ കമ്പ്യൂട്ടറുകളില്‍" + "എളുപ്പത്തില്‍ ഡെബിയന്‍ ഇന്‍സ്റ്റോള്‍ ചെയ്യാം (ഉച്ചക്ക് ശേഷം 2:30 നു്)," + "സ്വതന്ത്ര സോഫ്റ്റ്‌വെയറിൽ കേരളത്തിലെ സ്ത്രീകളുടെ പങ്കാളിത്തം (ഉച്ചക്ക്" + "ശേഷം 3:30 നു്), ഗ്നു/ലിനക്സും ഗെയ്മിങ്ങും (വൈകുന്നേരം 4:30 നു്)," + "കേരളത്തിലൊരു ഡെബ്കോൺഫ് (വൈകുന്നേരം 5:30 നു്) https://" + "debconf20.debconf.org/schedule/?block=7", + } + ] +} + mock_with_longer_content_detail = { "entries": [ { diff --git a/src/newsreader/news/collection/tests/feed/builder/tests.py b/src/newsreader/news/collection/tests/feed/builder/tests.py index c3e60e0..3a32e76 100644 --- a/src/newsreader/news/collection/tests/feed/builder/tests.py +++ b/src/newsreader/news/collection/tests/feed/builder/tests.py @@ -1,10 +1,7 @@ -from datetime import date, datetime, time -from unittest.mock import MagicMock +from datetime import datetime, timezone +from unittest.mock import Mock from django.test import TestCase -from django.utils import timezone - -import pytz from freezegun import freeze_time @@ -13,7 +10,23 @@ 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 * +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, +) @freeze_time("2019-10-30 12:30:00") @@ -21,271 +34,235 @@ class FeedBuilderTestCase(TestCase): def setUp(self): self.maxDiff = None - def test_basic_entry(self): - builder = FeedBuilder - rule = FeedFactory() - mock_stream = MagicMock(rule=rule) - - with builder((simple_mock, mock_stream)) as builder: - builder.save() - - post = Post.objects.get() - - publication_date = datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37) - ) - aware_date = pytz.utc.localize(publication_date) - - self.assertEquals(post.publication_date, aware_date) - self.assertEquals(Post.objects.count(), 1) - - self.assertEquals( - post.remote_identifier, - "https://www.bbc.co.uk/news/world-us-canada-48338168", - ) - - self.assertEquals( - post.url, "https://www.bbc.co.uk/news/world-us-canada-48338168" - ) - - self.assertEquals( - post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif" - ) - def test_multiple_entries(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((multiple_mock, mock_stream)) as builder: + with FeedBuilder(multiple_mock, mock_stream) as builder: + builder.build() builder.save() posts = Post.objects.order_by("-publication_date") - self.assertEquals(Post.objects.count(), 3) + self.assertEqual(Post.objects.count(), 3) post = posts[0] - publication_date = datetime.combine( - date(2019, 5, 20), time(hour=16, minute=32, second=38) + publication_date = datetime( + 2019, 5, 20, hour=16, minute=32, second=38, tzinfo=timezone.utc ) - aware_date = pytz.utc.localize(publication_date) - self.assertEquals( + self.assertEqual( post.publication_date.strftime("%Y-%m-%d %H:%M:%S"), - aware_date.strftime("%Y-%m-%d %H:%M:%S"), + publication_date.strftime("%Y-%m-%d %H:%M:%S"), ) - self.assertEquals( + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/uk-england-birmingham-48339080", ) - self.assertEquals( + self.assertEqual( post.url, "https://www.bbc.co.uk/news/uk-england-birmingham-48339080" ) - self.assertEquals( + self.assertEqual( post.title, "Birmingham head teacher threatened over LGBT lessons" ) post = posts[1] - publication_date = datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37) + publication_date = datetime( + 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc ) - aware_date = pytz.utc.localize(publication_date) - self.assertEquals( + self.assertEqual( post.publication_date.strftime("%Y-%m-%d %H:%M:%S"), - aware_date.strftime("%Y-%m-%d %H:%M:%S"), + publication_date.strftime("%Y-%m-%d %H:%M:%S"), ) - self.assertEquals( + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", ) - self.assertEquals( + self.assertEqual( post.url, "https://www.bbc.co.uk/news/world-us-canada-48338168" ) - self.assertEquals( + self.assertEqual( post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif" ) def test_entries_without_remote_identifier(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((mock_without_identifier, mock_stream)) as builder: + with FeedBuilder(mock_without_identifier, mock_stream) as builder: + builder.build() builder.save() posts = Post.objects.order_by("-publication_date") - self.assertEquals(Post.objects.count(), 2) + self.assertEqual(Post.objects.count(), 2) post = posts[0] - publication_date = datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37) + publication_date = datetime( + 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc ) - aware_date = pytz.utc.localize(publication_date) - self.assertEquals(post.publication_date, aware_date) - self.assertEquals(post.remote_identifier, None) - self.assertEquals( + self.assertEqual(post.publication_date, publication_date) + self.assertEqual(post.remote_identifier, None) + self.assertEqual( post.url, "https://www.bbc.co.uk/news/world-us-canada-48338168" ) - self.assertEquals( + self.assertEqual( post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif" ) post = posts[1] - publication_date = datetime.combine( - date(2019, 5, 20), time(hour=12, minute=19, second=19) + publication_date = datetime( + 2019, 5, 20, hour=12, minute=19, second=19, tzinfo=timezone.utc ) - aware_date = pytz.utc.localize(publication_date) - self.assertEquals(post.publication_date, aware_date) - self.assertEquals(post.remote_identifier, None) - self.assertEquals(post.url, "https://www.bbc.co.uk/news/technology-48334739") - self.assertEquals(post.title, "Huawei's Android loss: How it affects you") + self.assertEqual(post.publication_date, publication_date) + self.assertEqual(post.remote_identifier, None) + self.assertEqual(post.url, "https://www.bbc.co.uk/news/technology-48334739") + self.assertEqual(post.title, "Huawei's Android loss: How it affects you") def test_entry_without_publication_date(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((mock_without_publish_date, mock_stream)) as builder: + with FeedBuilder(mock_without_publish_date, mock_stream) as builder: + builder.build() builder.save() posts = Post.objects.order_by("-publication_date") - self.assertEquals(Post.objects.count(), 2) + self.assertEqual(Post.objects.count(), 2) post = posts[0] - self.assertEquals( + self.assertEqual( post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30" ) - self.assertEquals(post.created, timezone.now()) - self.assertEquals( + self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", ) post = posts[1] - self.assertEquals( + self.assertEqual( post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30" ) - self.assertEquals(post.created, timezone.now()) - self.assertEquals( + self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" ) def test_entry_without_url(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((mock_without_url, mock_stream)) as builder: + with FeedBuilder(mock_without_url, mock_stream) as builder: + builder.build() builder.save() posts = Post.objects.order_by("-publication_date") - self.assertEquals(Post.objects.count(), 2) + self.assertEqual(Post.objects.count(), 2) post = posts[0] - self.assertEquals(post.created, timezone.now()) - self.assertEquals( + self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", ) post = posts[1] - self.assertEquals(post.created, timezone.now()) - self.assertEquals( + self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" ) def test_entry_without_body(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((mock_without_body, mock_stream)) as builder: + with FeedBuilder(mock_without_body, mock_stream) as builder: + builder.build() builder.save() posts = Post.objects.order_by("-publication_date") - self.assertEquals(Post.objects.count(), 2) + self.assertEqual(Post.objects.count(), 2) post = posts[0] - self.assertEquals( + self.assertEqual( post.created.strftime("%Y-%m-%d %H:%M:%S"), "2019-10-30 12:30:00" ) - self.assertEquals( + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/uk-england-birmingham-48339080", ) - self.assertEquals(post.body, "") + self.assertEqual(post.body, "") post = posts[1] - self.assertEquals( + self.assertEqual( post.created.strftime("%Y-%m-%d %H:%M:%S"), "2019-10-30 12:30:00" ) - self.assertEquals( + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", ) - self.assertEquals(post.body, "") + self.assertEqual(post.body, "") def test_entry_without_author(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((mock_without_author, mock_stream)) as builder: + with FeedBuilder(mock_without_author, mock_stream) as builder: + builder.build() builder.save() posts = Post.objects.order_by("-publication_date") - self.assertEquals(Post.objects.count(), 2) + self.assertEqual(Post.objects.count(), 2) post = posts[0] - self.assertEquals(post.created, timezone.now()) - self.assertEquals( + self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/world-us-canada-48338168", ) - self.assertEquals(post.author, None) + self.assertEqual(post.author, None) post = posts[1] - self.assertEquals(post.created, timezone.now()) - self.assertEquals( + self.assertEqual(post.created, datetime.now(tz=timezone.utc)) + self.assertEqual( post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" ) - self.assertEquals(post.author, None) + self.assertEqual(post.author, None) def test_empty_entries(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((mock_without_entries, mock_stream)) as builder: + with FeedBuilder(mock_without_entries, mock_stream) as builder: + builder.build() builder.save() - self.assertEquals(Post.objects.count(), 0) + self.assertEqual(Post.objects.count(), 0) def test_update_entries(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) existing_first_post = FeedPostFactory.create( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule @@ -295,34 +272,35 @@ class FeedBuilderTestCase(TestCase): remote_identifier="a5479c66-8fae-11e9-8422-00163ef6bee7", rule=rule ) - with builder((mock_with_update_entries, mock_stream)) as builder: + with FeedBuilder(mock_with_update_entries, mock_stream) as builder: + builder.build() builder.save() - self.assertEquals(Post.objects.count(), 3) + self.assertEqual(Post.objects.count(), 3) existing_first_post.refresh_from_db() existing_second_post.refresh_from_db() - self.assertEquals( + self.assertEqual( existing_first_post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif", ) - self.assertEquals( + self.assertEqual( existing_second_post.title, "Huawei's Android loss: How it affects you" ) def test_html_sanitizing(self): - builder = FeedBuilder rule = FeedFactory() - mock_stream = MagicMock(rule=rule) + mock_stream = Mock(rule=rule) - with builder((mock_with_html, mock_stream)) as builder: + with FeedBuilder(mock_with_html, mock_stream) as builder: + builder.build() builder.save() post = Post.objects.get() - self.assertEquals(Post.objects.count(), 1) + self.assertEqual(Post.objects.count(), 1) self.assertTrue("
    " in post.body) self.assertTrue("

    " in post.body) @@ -335,44 +313,60 @@ class FeedBuilderTestCase(TestCase): self.assertTrue("", - "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", -} diff --git a/src/newsreader/news/collection/tests/reddit/builder/tests.py b/src/newsreader/news/collection/tests/reddit/builder/tests.py deleted file mode 100644 index 9c1a046..0000000 --- a/src/newsreader/news/collection/tests/reddit/builder/tests.py +++ /dev/null @@ -1,408 +0,0 @@ -from datetime import datetime -from unittest.mock import MagicMock - -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 = MagicMock(rule=subreddit) - - with builder((simple_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((empty_mock, mock_stream)) as builder: - builder.save() - - self.assertEquals(Post.objects.count(), 0) - - def test_unknown_mock(self): - builder = RedditBuilder - - subreddit = SubredditFactory() - mock_stream = MagicMock(rule=subreddit) - - with builder((unknown_mock, mock_stream)) as builder: - builder.save() - - self.assertEquals(Post.objects.count(), 0) - - def test_update_posts(self): - subreddit = SubredditFactory() - existing_post = RedditPostFactory( - remote_identifier="hm0qct", - author="Old author", - title="Old title", - body="Old body", - url="https://bbc.com/", - rule=subreddit, - ) - - builder = RedditBuilder - mock_stream = MagicMock(rule=subreddit) - - with builder((simple_mock, mock_stream)) as builder: - builder.save() - - posts = {post.remote_identifier: post for post in Post.objects.all()} - - self.assertCountEqual( - ("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys() - ) - - existing_post.refresh_from_db() - - self.assertEquals(existing_post.remote_identifier, "hm0qct") - self.assertEquals(existing_post.author, "AutoModerator") - self.assertEquals( - existing_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 time.", - existing_post.body, - ) - self.assertEquals( - existing_post.publication_date, - pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22)), - ) - self.assertEquals( - existing_post.url, - "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", - ) - - def test_html_sanitizing(self): - builder = RedditBuilder - - subreddit = SubredditFactory() - mock_stream = MagicMock(rule=subreddit) - - with builder((unsanitized_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((author_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((title_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((duplicate_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - duplicate_post = RedditPostFactory( - remote_identifier="hm0qct", rule=subreddit, title="foo" - ) - - with builder((simple_mock, mock_stream)) as builder: - 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() - ) - - duplicate_post.refresh_from_db() - - self.assertEquals( - duplicate_post.publication_date, - pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22)), - ) - self.assertEquals( - duplicate_post.title, - "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", - ) - - def test_image_post(self): - builder = RedditBuilder - - subreddit = SubredditFactory() - mock_stream = MagicMock(rule=subreddit) - - with builder((image_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((external_image_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((video_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((external_video_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((external_gifv_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((simple_mock, mock_stream)) as builder: - 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 = MagicMock(rule=subreddit) - - with builder((unknown_mock, mock_stream)) as builder: - builder.save() - - self.assertEquals(Post.objects.count(), 0) diff --git a/src/newsreader/news/collection/tests/reddit/client/__init__.py b/src/newsreader/news/collection/tests/reddit/client/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/newsreader/news/collection/tests/reddit/client/mocks.py b/src/newsreader/news/collection/tests/reddit/client/mocks.py deleted file mode 100644 index 6a11409..0000000 --- a/src/newsreader/news/collection/tests/reddit/client/mocks.py +++ /dev/null @@ -1,160 +0,0 @@ -# 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 deleted file mode 100644 index f2ee84d..0000000 --- a/src/newsreader/news/collection/tests/reddit/client/tests.py +++ /dev/null @@ -1,164 +0,0 @@ -from unittest.mock import MagicMock, 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 = MagicMock(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 = MagicMock(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 deleted file mode 100644 index e69de29..0000000 diff --git a/src/newsreader/news/collection/tests/reddit/collector/mocks.py b/src/newsreader/news/collection/tests/reddit/collector/mocks.py deleted file mode 100644 index 37d40d8..0000000 --- a/src/newsreader/news/collection/tests/reddit/collector/mocks.py +++ /dev/null @@ -1,1662 +0,0 @@ -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 deleted file mode 100644 index 1fd18b0..0000000 --- a/src/newsreader/news/collection/tests/reddit/collector/tests.py +++ /dev/null @@ -1,204 +0,0 @@ -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_suceeded, 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_suceeded, 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 deleted file mode 100644 index e69de29..0000000 diff --git a/src/newsreader/news/collection/tests/reddit/stream/mocks.py b/src/newsreader/news/collection/tests/reddit/stream/mocks.py deleted file mode 100644 index 148b31a..0000000 --- a/src/newsreader/news/collection/tests/reddit/stream/mocks.py +++ /dev/null @@ -1,3289 +0,0 @@ -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 deleted file mode 100644 index 19aff61..0000000 --- a/src/newsreader/news/collection/tests/reddit/stream/tests.py +++ /dev/null @@ -1,144 +0,0 @@ -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 deleted file mode 100644 index cd062b6..0000000 --- a/src/newsreader/news/collection/tests/reddit/test_scheduler.py +++ /dev/null @@ -1,142 +0,0 @@ -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_suceeded=timezone.now() - timedelta(days=4), - enabled=True, - ), - CollectionRuleFactory( - user=user_1, - type=RuleTypeChoices.subreddit, - last_suceeded=timezone.now() - timedelta(days=3), - enabled=True, - ), - CollectionRuleFactory( - user=user_1, - type=RuleTypeChoices.subreddit, - last_suceeded=timezone.now() - timedelta(days=2), - enabled=True, - ), - ] - - user_2_rules = [ - CollectionRuleFactory( - user=user_2, - type=RuleTypeChoices.subreddit, - last_suceeded=timezone.now() - timedelta(days=4), - enabled=True, - ), - CollectionRuleFactory( - user=user_2, - type=RuleTypeChoices.subreddit, - last_suceeded=timezone.now() - timedelta(days=3), - enabled=True, - ), - CollectionRuleFactory( - user=user_2, - type=RuleTypeChoices.subreddit, - last_suceeded=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_suceeded=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_suceeded=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/tests.py b/src/newsreader/news/collection/tests/tests.py index 363e0b5..c7f0bb0 100644 --- a/src/newsreader/news/collection/tests/tests.py +++ b/src/newsreader/news/collection/tests/tests.py @@ -1,10 +1,9 @@ -from unittest.mock import MagicMock, patch +from unittest.mock import Mock, patch from django.test import TestCase from bs4 import BeautifulSoup -from newsreader.news.collection.base import URLBuilder, WebsiteStream from newsreader.news.collection.exceptions import ( StreamDeniedException, StreamException, @@ -13,6 +12,7 @@ from newsreader.news.collection.exceptions import ( StreamParseException, StreamTimeOutException, ) +from newsreader.news.collection.favicon import WebsiteStream, WebsiteURLBuilder from newsreader.news.collection.tests.factories import CollectionRuleFactory from .mocks import feed_mock_without_link, simple_feed_mock, simple_mock @@ -20,117 +20,125 @@ from .mocks import feed_mock_without_link, simple_feed_mock, simple_mock class WebsiteStreamTestCase(TestCase): def setUp(self): - self.patched_fetch = patch("newsreader.news.collection.base.fetch") + self.patched_fetch = patch("newsreader.news.collection.favicon.fetch") self.mocked_fetch = self.patched_fetch.start() def tearDown(self): patch.stopall() def test_simple(self): - self.mocked_fetch.return_value = MagicMock(content=simple_mock) + self.mocked_fetch.return_value = Mock(content=simple_mock) - rule = CollectionRuleFactory() - stream = WebsiteStream(rule.url) + rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/") + stream = WebsiteStream(rule) return_value = stream.read() - self.mocked_fetch.assert_called_once_with(rule.url) - self.assertEquals(return_value, (BeautifulSoup(simple_mock, "lxml"), stream)) + self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/") + self.assertEquals( + return_value, (BeautifulSoup(simple_mock, features="lxml"), stream) + ) def test_raises_exception(self): self.mocked_fetch.side_effect = StreamException - rule = CollectionRuleFactory() - stream = WebsiteStream(rule.url) + rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/") + stream = WebsiteStream(rule) with self.assertRaises(StreamException): stream.read() - self.mocked_fetch.assert_called_once_with(rule.url) + self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/") def test_raises_denied_exception(self): self.mocked_fetch.side_effect = StreamDeniedException - rule = CollectionRuleFactory() - stream = WebsiteStream(rule.url) + rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/") + stream = WebsiteStream(rule) with self.assertRaises(StreamDeniedException): stream.read() - self.mocked_fetch.assert_called_once_with(rule.url) + self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/") def test_raises_stream_not_found_exception(self): self.mocked_fetch.side_effect = StreamNotFoundException - rule = CollectionRuleFactory() - stream = WebsiteStream(rule.url) + rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/") + stream = WebsiteStream(rule) with self.assertRaises(StreamNotFoundException): stream.read() - self.mocked_fetch.assert_called_once_with(rule.url) + self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/") def test_stream_raises_time_out_exception(self): self.mocked_fetch.side_effect = StreamTimeOutException - rule = CollectionRuleFactory() - stream = WebsiteStream(rule.url) + rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/") + stream = WebsiteStream(rule) with self.assertRaises(StreamTimeOutException): stream.read() - self.mocked_fetch.assert_called_once_with(rule.url) + self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/") def test_stream_raises_forbidden_exception(self): self.mocked_fetch.side_effect = StreamForbiddenException - rule = CollectionRuleFactory() - stream = WebsiteStream(rule.url) + rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/") + stream = WebsiteStream(rule) with self.assertRaises(StreamForbiddenException): stream.read() - self.mocked_fetch.assert_called_once_with(rule.url) + self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/") - @patch("newsreader.news.collection.base.WebsiteStream.parse") + @patch("newsreader.news.collection.favicon.WebsiteStream.parse") def test_stream_raises_parse_exception(self, mocked_parse): - self.mocked_fetch.return_value = MagicMock() + self.mocked_fetch.return_value = Mock() mocked_parse.side_effect = StreamParseException - rule = CollectionRuleFactory() - stream = WebsiteStream(rule.url) + rule = CollectionRuleFactory(website_url="https://www.bbc.co.uk/news/") + stream = WebsiteStream(rule) with self.assertRaises(StreamParseException): stream.read() - self.mocked_fetch.assert_called_once_with(rule.url) + self.mocked_fetch.assert_called_once_with("https://www.bbc.co.uk/news/") -class URLBuilderTestCase(TestCase): +class WebsiteURLBuilderTestCase(TestCase): def test_simple(self): initial_rule = CollectionRuleFactory() - with URLBuilder((simple_feed_mock, MagicMock(rule=initial_rule))) as builder: - rule, url = builder.build() + with WebsiteURLBuilder(simple_feed_mock, Mock(rule=initial_rule)) as builder: + builder.build() + builder.save() - self.assertEquals(rule.pk, initial_rule.pk) - self.assertEquals(url, "https://www.bbc.co.uk/news/") + initial_rule.refresh_from_db() + + self.assertEquals(initial_rule.website_url, "https://www.bbc.co.uk/news/") def test_no_link(self): - initial_rule = CollectionRuleFactory() + initial_rule = CollectionRuleFactory(website_url=None) - with URLBuilder( - (feed_mock_without_link, MagicMock(rule=initial_rule)) + with WebsiteURLBuilder( + feed_mock_without_link, Mock(rule=initial_rule) ) as builder: - rule, url = builder.build() + builder.build() + builder.save() - self.assertEquals(rule.pk, initial_rule.pk) - self.assertEquals(url, None) + initial_rule.refresh_from_db() + + self.assertEquals(initial_rule.website_url, None) def test_no_data(self): - initial_rule = CollectionRuleFactory() + initial_rule = CollectionRuleFactory(website_url=None) - with URLBuilder((None, MagicMock(rule=initial_rule))) as builder: - rule, url = builder.build() + with WebsiteURLBuilder(None, Mock(rule=initial_rule)) as builder: + builder.build() + builder.save() - self.assertEquals(rule.pk, initial_rule.pk) - self.assertEquals(url, None) + initial_rule.refresh_from_db() + + self.assertEquals(initial_rule.website_url, None) diff --git a/src/newsreader/news/collection/tests/utils/tests.py b/src/newsreader/news/collection/tests/utils/tests.py index 10013c3..e88d1bf 100644 --- a/src/newsreader/news/collection/tests/utils/tests.py +++ b/src/newsreader/news/collection/tests/utils/tests.py @@ -1,4 +1,4 @@ -from unittest.mock import MagicMock, patch +from unittest.mock import Mock, patch from django.test import TestCase @@ -19,7 +19,7 @@ from newsreader.news.collection.utils import fetch, post class HelperFunctionTestCase: def test_simple(self): - self.mocked_method.return_value = MagicMock(status_code=200, content="content") + self.mocked_method.return_value = Mock(status_code=200, content="content") url = "https://www.bbc.co.uk/news" response = self.method(url) @@ -27,7 +27,7 @@ class HelperFunctionTestCase: self.assertEquals(response.content, "content") def test_raises_not_found(self): - self.mocked_method.return_value = MagicMock(status_code=404) + self.mocked_method.return_value = Mock(status_code=404) url = "https://www.bbc.co.uk/news" @@ -35,7 +35,7 @@ class HelperFunctionTestCase: self.method(url) def test_raises_denied(self): - self.mocked_method.return_value = MagicMock(status_code=401) + self.mocked_method.return_value = Mock(status_code=401) url = "https://www.bbc.co.uk/news" @@ -43,7 +43,7 @@ class HelperFunctionTestCase: self.method(url) def test_raises_forbidden(self): - self.mocked_method.return_value = MagicMock(status_code=403) + self.mocked_method.return_value = Mock(status_code=403) url = "https://www.bbc.co.uk/news" @@ -51,7 +51,7 @@ class HelperFunctionTestCase: self.method(url) def test_raises_timed_out(self): - self.mocked_method.return_value = MagicMock(status_code=408) + self.mocked_method.return_value = Mock(status_code=408) url = "https://www.bbc.co.uk/news" @@ -99,7 +99,7 @@ class HelperFunctionTestCase: self.method(url) def test_raises_stream_error_on_too_many_requests(self): - self.mocked_method.return_value = MagicMock(status_code=429) + self.mocked_method.return_value = Mock(status_code=429) url = "https://www.bbc.co.uk/news" diff --git a/src/newsreader/news/collection/tests/views/base.py b/src/newsreader/news/collection/tests/views/base.py index d7de171..a651719 100644 --- a/src/newsreader/news/collection/tests/views/base.py +++ b/src/newsreader/news/collection/tests/views/base.py @@ -46,10 +46,9 @@ class CollectionRuleViewTestCase: name="new name", category=other_rule.category, url=other_rule.url, - timezone=other_rule.timezone, ) - other_url = reverse("news:collection:rule-update", args=[other_rule.pk]) + other_url = reverse("news:collection:feed-update", args=[other_rule.pk]) response = self.client.post(other_url, self.form_data) self.assertEquals(response.status_code, 404) diff --git a/src/newsreader/news/collection/tests/views/test_crud.py b/src/newsreader/news/collection/tests/views/test_crud.py index 61f6835..d4bd731 100644 --- a/src/newsreader/news/collection/tests/views/test_crud.py +++ b/src/newsreader/news/collection/tests/views/test_crud.py @@ -1,7 +1,7 @@ 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 @@ -10,59 +10,62 @@ from newsreader.news.collection.tests.views.base import CollectionRuleViewTestCa from newsreader.news.core.tests.factories import CategoryFactory -class CollectionRuleCreateViewTestCase(CollectionRuleViewTestCase, TestCase): +class FeedCreateViewTestCase(CollectionRuleViewTestCase, TestCase): def setUp(self): super().setUp() - self.url = reverse("news:collection:rule-create") + self.url = reverse("news:collection:feed-create") 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.assertEquals(response.status_code, 302) + self.assertEqual(response.status_code, 302) rule = CollectionRule.objects.get(name="new rule") - 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.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.assertTrue( + PeriodicTask.objects.get( + name=f"{self.user.email}-feed", task="FeedTask", enabled=True + ) + ) -class CollectionRuleUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): +class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): def setUp(self): super().setUp() self.rule = FeedFactory( name="collection rule", user=self.user, category=self.category ) - self.url = reverse("news:collection:rule-update", kwargs={"pk": self.rule.pk}) + self.url = reverse("news:collection:feed-update", kwargs={"pk": self.rule.pk}) self.form_data.update( 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.assertEquals(response.status_code, 302) + self.assertEqual(response.status_code, 302) self.rule.refresh_from_db() - self.assertEquals(self.rule.name, "new name") + self.assertEqual(self.rule.name, "new name") def test_category_change(self): new_category = CategoryFactory(user=self.user) @@ -70,32 +73,18 @@ class CollectionRuleUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): self.form_data.update(category=new_category.pk) response = self.client.post(self.url, self.form_data) - self.assertEquals(response.status_code, 302) + self.assertEqual(response.status_code, 302) self.rule.refresh_from_db() - self.assertEquals(self.rule.category.pk, new_category.pk) + self.assertEqual(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.assertEquals(response.status_code, 302) + self.assertEqual(response.status_code, 302) self.rule.refresh_from_db() - 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:rule-update", kwargs={"pk": rule.pk}) - - response = self.client.get(url) - - self.assertEquals(response.status_code, 404) + self.assertEqual(self.rule.category, None) diff --git a/src/newsreader/news/collection/tests/views/test_import_view.py b/src/newsreader/news/collection/tests/views/test_import_view.py index f4188e7..a1f0017 100644 --- a/src/newsreader/news/collection/tests/views/test_import_view.py +++ b/src/newsreader/news/collection/tests/views/test_import_view.py @@ -84,7 +84,7 @@ class OPMLImportTestCase(TestCase): rules = CollectionRule.objects.all() self.assertEquals(len(rules), 0) - self.assertFormError(response, "form", "file", _("No (new) rules found")) + self.assertFormError(response, "form", "file", _("No (new) feeds found")) def test_invalid_feeds(self): file_path = self._get_file_path("invalid-url-feeds.opml") @@ -99,7 +99,7 @@ class OPMLImportTestCase(TestCase): rules = CollectionRule.objects.all() self.assertEquals(len(rules), 0) - self.assertFormError(response, "form", "file", _("No (new) rules found")) + self.assertFormError(response, "form", "file", _("No (new) feeds found")) def test_invalid_file(self): file_path = self._get_file_path("test.png") diff --git a/src/newsreader/news/collection/tests/views/test_subreddit_views.py b/src/newsreader/news/collection/tests/views/test_subreddit_views.py deleted file mode 100644 index 0dff663..0000000 --- a/src/newsreader/news/collection/tests/views/test_subreddit_views.py +++ /dev/null @@ -1,128 +0,0 @@ -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), - } - - 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, - } - - 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/urls.py b/src/newsreader/news/collection/urls.py index 5253210..a57a00e 100644 --- a/src/newsreader/news/collection/urls.py +++ b/src/newsreader/news/collection/urls.py @@ -3,7 +3,6 @@ from django.urls import path from newsreader.news.collection.endpoints import ( DetailRuleView, - ListRuleView, NestedRuleView, RuleReadView, ) @@ -11,12 +10,10 @@ from newsreader.news.collection.views import ( CollectionRuleBulkDeleteView, CollectionRuleBulkDisableView, CollectionRuleBulkEnableView, - CollectionRuleCreateView, CollectionRuleListView, - CollectionRuleUpdateView, + FeedCreateView, + FeedUpdateView, OPMLImportView, - SubRedditCreateView, - SubRedditUpdateView, ) @@ -24,21 +21,16 @@ endpoints = [ path("rules//", DetailRuleView.as_view(), name="rules-detail"), path("rules//posts/", NestedRuleView.as_view(), name="rules-nested-posts"), path("rules//read/", RuleReadView.as_view(), name="rules-read"), - path("rules/", ListRuleView.as_view(), name="rules-list"), ] urlpatterns = [ + # Feeds + path( + "feeds//", login_required(FeedUpdateView.as_view()), name="feed-update" + ), + path("feeds/create/", login_required(FeedCreateView.as_view()), name="feed-create"), + # Generic rules path("rules/", login_required(CollectionRuleListView.as_view()), name="rules"), - path( - "rules//", - login_required(CollectionRuleUpdateView.as_view()), - name="rule-update", - ), - path( - "rules/create/", - login_required(CollectionRuleCreateView.as_view()), - name="rule-create", - ), path( "rules/delete/", login_required(CollectionRuleBulkDeleteView.as_view()), @@ -54,15 +46,5 @@ urlpatterns = [ login_required(CollectionRuleBulkDisableView.as_view()), name="rules-disable", ), - path( - "rules/subreddits/create/", - login_required(SubRedditCreateView.as_view()), - name="subreddit-create", - ), - path( - "rules/subreddits//", - login_required(SubRedditUpdateView.as_view()), - name="subreddit-update", - ), path("rules/import/", login_required(OPMLImportView.as_view()), name="import"), ] diff --git a/src/newsreader/news/collection/utils.py b/src/newsreader/news/collection/utils.py index d47cd68..36a3b9e 100644 --- a/src/newsreader/news/collection/utils.py +++ b/src/newsreader/news/collection/utils.py @@ -1,11 +1,8 @@ -from datetime import datetime +from datetime import datetime, timezone from django.conf import settings from django.db.models.fields import CharField, TextField -from django.template.defaultfilters import truncatechars -from django.utils import timezone -import pytz import requests from requests.exceptions import RequestException @@ -16,22 +13,19 @@ from newsreader.news.collection.response_handler import ResponseHandler DEFAULT_HEADERS = {"User-Agent": f"linux:rss.fudiggity.nl:{settings.VERSION}"} -def build_publication_date(dt, tz): +def build_publication_date(_datetime_info: tuple) -> datetime: try: - naive_datetime = datetime(*dt[:6]) - published_parsed = timezone.make_aware(naive_datetime, timezone=tz) + return datetime(*_datetime_info[:6], tzinfo=timezone.utc) except (TypeError, ValueError): - return timezone.now() - - return published_parsed.astimezone(pytz.utc) + return datetime.now(tz=timezone.utc) -def fetch(url, headers={}): +def fetch(url, auth=None, headers={}): headers = {**DEFAULT_HEADERS, **headers} with ResponseHandler() as response_handler: try: - response = requests.get(url, headers=headers) + response = requests.get(url, auth=auth, headers=headers) response_handler.handle_response(response) except RequestException as exception: response_handler.map_exception(exception) @@ -66,6 +60,6 @@ def truncate_text(cls, field_name, value): return value if len(value) > max_length: - return truncatechars(value, max_length) + 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 20769f3..dc92557 100644 --- a/src/newsreader/news/collection/views/__init__.py +++ b/src/newsreader/news/collection/views/__init__.py @@ -1,13 +1,22 @@ -from newsreader.news.collection.views.reddit import ( - SubRedditCreateView, - SubRedditUpdateView, +from newsreader.news.collection.views.feed import ( + FeedCreateView, + FeedUpdateView, + OPMLImportView, ) from newsreader.news.collection.views.rules import ( CollectionRuleBulkDeleteView, CollectionRuleBulkDisableView, CollectionRuleBulkEnableView, - CollectionRuleCreateView, CollectionRuleListView, - CollectionRuleUpdateView, - OPMLImportView, ) + + +__all__ = [ + "FeedCreateView", + "FeedUpdateView", + "OPMLImportView", + "CollectionRuleBulkDeleteView", + "CollectionRuleBulkDisableView", + "CollectionRuleBulkEnableView", + "CollectionRuleListView", +] diff --git a/src/newsreader/news/collection/views/base.py b/src/newsreader/news/collection/views/base.py index e7f7b63..afce363 100644 --- a/src/newsreader/news/collection/views/base.py +++ b/src/newsreader/news/collection/views/base.py @@ -1,13 +1,17 @@ +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.forms import CollectionRuleForm from newsreader.news.collection.models import CollectionRule from newsreader.news.core.models import Category +from newsreader.utils.views import NavListMixin -class CollectionRuleViewMixin: +class CollectionRuleViewMixin(NavListMixin): queryset = CollectionRule.objects.order_by("name") def get_queryset(self): @@ -17,13 +21,12 @@ class CollectionRuleViewMixin: class CollectionRuleDetailMixin: success_url = reverse_lazy("news:collection:rules") - form_class = CollectionRuleForm def get_context_data(self, **kwargs): context_data = super().get_context_data(**kwargs) categories = Category.objects.filter(user=self.request.user).order_by("name") - timezones = [timezone for timezone in pytz.all_timezones] + timezones = available_timezones() context_data["categories"] = categories context_data["timezones"] = timezones @@ -34,3 +37,25 @@ class CollectionRuleDetailMixin: kwargs = super().get_form_kwargs() kwargs["user"] = self.request.user return kwargs + + +class TaskCreationMixin: + def form_valid(self, form): + response = super().form_valid(form) + + interval, period = self.task_interval + task_interval, _ = IntervalSchedule.objects.get_or_create( + every=interval, period=period + ) + + PeriodicTask.objects.get_or_create( + name=f"{self.request.user.email}-{self.task_name}", + task=self.task_type, + defaults={ + "args": json.dumps([self.request.user.pk]), + "interval": task_interval, + "enabled": True, + }, + ) + + return response diff --git a/src/newsreader/news/collection/views/feed.py b/src/newsreader/news/collection/views/feed.py new file mode 100644 index 0000000..88c80e7 --- /dev/null +++ b/src/newsreader/news/collection/views/feed.py @@ -0,0 +1,67 @@ +from django.contrib import messages +from django.urls import reverse +from django.utils.translation import gettext as _ +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.models import CollectionRule +from newsreader.news.collection.views.base import ( + CollectionRuleDetailMixin, + CollectionRuleViewMixin, + TaskCreationMixin, +) +from newsreader.utils.opml import parse_opml +from newsreader.utils.views import NavListMixin + + +class FeedUpdateView(CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView): + template_name = "news/collection/views/feed-update.html" + context_object_name = "feed" + form_class = FeedForm + + def get_queryset(self): + queryset = super().get_queryset() + return queryset.filter(type=RuleTypeChoices.feed) + + +class FeedCreateView( + CollectionRuleViewMixin, CollectionRuleDetailMixin, TaskCreationMixin, CreateView +): + template_name = "news/collection/views/feed-create.html" + task_interval = (1, IntervalSchedule.HOURS) + task_name = "feed" + task_type = "FeedTask" + form_class = FeedForm + + +class OPMLImportView(NavListMixin, FormView): + form_class = OPMLImportForm + template_name = "news/collection/views/import.html" + + def form_valid(self, form): + user = self.request.user + file = form.cleaned_data["file"] + skip_existing = form.cleaned_data["skip_existing"] + + instances = parse_opml(file, user, skip_existing=skip_existing) + + try: + feeds = CollectionRule.objects.bulk_create(instances) + except IOError: + form.add_error("file", _("Invalid OPML file")) + return self.form_invalid(form) + + if not feeds: + form.add_error("file", _("No (new) feeds found")) + return self.form_invalid(form) + + message = _(f"{len(feeds)} new feeds created") + messages.success(self.request, message) + + return super().form_valid(form) + + def get_success_url(self): + return reverse("news:collection:rules") diff --git a/src/newsreader/news/collection/views/reddit.py b/src/newsreader/news/collection/views/reddit.py deleted file mode 100644 index 62ec408..0000000 --- a/src/newsreader/news/collection/views/reddit.py +++ /dev/null @@ -1,27 +0,0 @@ -from django.views.generic.edit import CreateView, UpdateView - -from newsreader.news.collection.choices import RuleTypeChoices -from newsreader.news.collection.forms import SubRedditRuleForm -from newsreader.news.collection.views.base import ( - CollectionRuleDetailMixin, - CollectionRuleViewMixin, -) - - -class SubRedditCreateView( - CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView -): - form_class = SubRedditRuleForm - template_name = "news/collection/views/subreddit-create.html" - - -class SubRedditUpdateView( - CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView -): - form_class = SubRedditRuleForm - 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 e020b67..753d9d7 100644 --- a/src/newsreader/news/collection/views/rules.py +++ b/src/newsreader/news/collection/views/rules.py @@ -2,43 +2,21 @@ from django.contrib import messages from django.shortcuts import redirect from django.urls import reverse from django.utils.translation import gettext as _ -from django.views.generic.edit import CreateView, FormView, UpdateView +from django.views.generic.edit import FormView from django.views.generic.list import ListView -from newsreader.news.collection.choices import RuleTypeChoices -from newsreader.news.collection.forms import CollectionRuleBulkForm, OPMLImportForm -from newsreader.news.collection.models import CollectionRule -from newsreader.news.collection.views.base import ( - CollectionRuleDetailMixin, - CollectionRuleViewMixin, -) -from newsreader.utils.opml import parse_opml +from newsreader.news.collection.forms import CollectionRuleBulkForm +from newsreader.news.collection.views.base import CollectionRuleViewMixin +from newsreader.utils.views import NavListMixin -class CollectionRuleListView(CollectionRuleViewMixin, ListView): +class CollectionRuleListView(CollectionRuleViewMixin, NavListMixin, ListView): paginate_by = 50 template_name = "news/collection/views/rules.html" context_object_name = "rules" -class CollectionRuleUpdateView( - CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView -): - template_name = "news/collection/views/rule-update.html" - context_object_name = "rule" - - def get_queryset(self): - queryset = super().get_queryset() - return queryset.filter(type=RuleTypeChoices.feed) - - -class CollectionRuleCreateView( - CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView -): - template_name = "news/collection/views/rule-create.html" - - -class CollectionRuleBulkView(FormView): +class CollectionRuleBulkView(NavListMixin, FormView): form_class = CollectionRuleBulkForm def get_redirect_url(self): @@ -90,33 +68,3 @@ class CollectionRuleBulkDeleteView(CollectionRuleBulkView): rule.delete() return response - - -class OPMLImportView(FormView): - form_class = OPMLImportForm - template_name = "news/collection/views/import.html" - - def form_valid(self, form): - user = self.request.user - file = form.cleaned_data["file"] - skip_existing = form.cleaned_data["skip_existing"] - - instances = parse_opml(file, user, skip_existing=skip_existing) - - try: - rules = CollectionRule.objects.bulk_create(instances) - except IOError: - form.add_error("file", _("Invalid OPML file")) - return self.form_invalid(form) - - if not rules: - form.add_error("file", _("No (new) rules found")) - return self.form_invalid(form) - - message = _(f"{len(rules)} new rules created") - messages.success(self.request, message) - - return super().form_valid(form) - - def get_success_url(self): - return reverse("news:collection:rules") diff --git a/src/newsreader/news/core/apps.py b/src/newsreader/news/core/apps.py index 5ef1d60..a120e96 100644 --- a/src/newsreader/news/core/apps.py +++ b/src/newsreader/news/core/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class CoreConfig(AppConfig): - name = "core" + name = "newsreader.news.core" diff --git a/src/newsreader/news/core/endpoints.py b/src/newsreader/news/core/endpoints.py index f5a48bc..092ec6f 100644 --- a/src/newsreader/news/core/endpoints.py +++ b/src/newsreader/news/core/endpoints.py @@ -1,10 +1,9 @@ -from django.db.models import Q +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,28 +12,20 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from newsreader.accounts.permissions import IsPostOwner -from newsreader.core.pagination import LargeResultSetPagination +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 ReadFilter +from newsreader.news.core.filters import SavedFilter from newsreader.news.core.models import Category, Post from newsreader.news.core.serializers import CategorySerializer, PostSerializer class ListPostView(ListAPIView): - queryset = Post.objects.all() + queryset = Post.objects.filter(read=False) serializer_class = PostSerializer - pagination_class = LargeResultSetPagination - filter_backends = [ReadFilter] - - def get_queryset(self): - user = self.request.user - queryset = ( - self.queryset.filter(rule__user=user) - .filter(Q(rule__category=None) | Q(rule__category__user=user)) - .order_by("rule", "-publication_date", "-created") - ) - - return queryset + permission_classes = (IsAuthenticated, IsPostOwner) + pagination_class = CursorPagination + filter_backends = [SavedFilter] class DetailPostView(RetrieveUpdateAPIView): @@ -75,10 +66,8 @@ class NestedRuleCategoryView(ListAPIView): class NestedPostCategoryView(ListAPIView): - queryset = Category.objects.prefetch_related("rules", "rules__posts").all() serializer_class = PostSerializer - pagination_class = LargeResultSetPagination - filter_backends = [ReadFilter] + pagination_class = CursorPagination def get_queryset(self): lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field @@ -87,14 +76,16 @@ class NestedPostCategoryView(ListAPIView): # filtered on the user. filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} - category = get_object_or_404(self.queryset, **filter_kwargs) + 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) + self.check_object_permissions(self.request, category) - queryset = Post.objects.filter( - rule__in=category.rules.values_list("id", flat=True) - ).order_by("-publication_date", "rule__name") - - return queryset + return Post.objects.filter(rule__in=category.user_rules, read=False) class CategoryReadView(GenericAPIView): diff --git a/src/newsreader/news/core/filters.py b/src/newsreader/news/core/filters.py index d322d83..9883304 100644 --- a/src/newsreader/news/core/filters.py +++ b/src/newsreader/news/core/filters.py @@ -1,12 +1,11 @@ -from django.utils.encoding import force_text -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework import filters from rest_framework.compat import coreapi, coreschema -class ReadFilter(filters.BaseFilterBackend): - query_param = "read" +class SavedFilter(filters.BaseFilterBackend): + query_param = "saved" def filter_queryset(self, request, queryset, view): key = request.query_params.get(self.query_param, None) @@ -16,7 +15,7 @@ class ReadFilter(filters.BaseFilterBackend): return queryset value = available_values[key] - return queryset.filter(read=value) + return queryset.filter(saved=value) def get_schema_fields(self, view): return [ @@ -25,8 +24,8 @@ class ReadFilter(filters.BaseFilterBackend): required=False, location="query", schema=coreschema.String( - title=force_text(self.query_param), - description=force_text(_("Wether posts should be read or not")), + title=str(self.query_param), + description=str(_("Wether posts should be saved or not")), ), ) ] diff --git a/src/newsreader/news/core/forms.py b/src/newsreader/news/core/forms.py index a08022a..5dcde94 100644 --- a/src/newsreader/news/core/forms.py +++ b/src/newsreader/news/core/forms.py @@ -12,7 +12,14 @@ class RulesWidget(CheckboxSelectMultiple): def create_option(self, *args, **kwargs): option = super().create_option(*args, **kwargs) - instance = self.choices.queryset.get(pk=option["value"]) + + # see https://docs.djangoproject.com/en/3.1/releases/3.1/#id1 + try: + pk = int(option["value"]) + except TypeError: + pk = option["value"].value + + instance = self.choices.queryset.get(pk=pk) if self.category and instance.category: option["selected"] = self.category.pk == instance.category.pk diff --git a/src/newsreader/news/core/migrations/0001_initial.py b/src/newsreader/news/core/migrations/0001_initial.py index eb74fc7..2fe963f 100644 --- a/src/newsreader/news/core/migrations/0001_initial.py +++ b/src/newsreader/news/core/migrations/0001_initial.py @@ -8,7 +8,6 @@ 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 4d9ad4f..5335614 100644 --- a/src/newsreader/news/core/migrations/0002_auto_20190714_1425.py +++ b/src/newsreader/news/core/migrations/0002_auto_20190714_1425.py @@ -7,7 +7,6 @@ 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 8306051..245c545 100644 --- a/src/newsreader/news/core/migrations/0003_post_read.py +++ b/src/newsreader/news/core/migrations/0003_post_read.py @@ -4,7 +4,6 @@ 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 04c6fc7..19d6bd3 100644 --- a/src/newsreader/news/core/migrations/0004_auto_20191116_1315.py +++ b/src/newsreader/news/core/migrations/0004_auto_20191116_1315.py @@ -5,7 +5,6 @@ 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 0010448..dbf07fb 100644 --- a/src/newsreader/news/core/migrations/0005_auto_20200412_1955.py +++ b/src/newsreader/news/core/migrations/0005_auto_20200412_1955.py @@ -7,7 +7,6 @@ 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 f90b205..94c716e 100644 --- a/src/newsreader/news/core/migrations/0006_auto_20200524_1218.py +++ b/src/newsreader/news/core/migrations/0006_auto_20200524_1218.py @@ -6,7 +6,6 @@ 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 751faf9..7721462 100644 --- a/src/newsreader/news/core/migrations/0007_auto_20200706_2312.py +++ b/src/newsreader/news/core/migrations/0007_auto_20200706_2312.py @@ -4,7 +4,6 @@ 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 new file mode 100644 index 0000000..40fbd14 --- /dev/null +++ b/src/newsreader/news/core/migrations/0008_post_saved.py @@ -0,0 +1,13 @@ +# Generated by Django 3.1.5 on 2021-02-19 20:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("core", "0007_auto_20200706_2312")] + + operations = [ + migrations.AddField( + model_name="post", name="saved", field=models.BooleanField(default=False) + ) + ] diff --git a/src/newsreader/news/core/models.py b/src/newsreader/news/core/models.py index ff44c81..2f7d571 100644 --- a/src/newsreader/news/core/models.py +++ b/src/newsreader/news/core/models.py @@ -14,6 +14,7 @@ class Post(TimeStampedModel): url = models.URLField(max_length=1024, blank=True, null=True) read = models.BooleanField(default=False) + saved = models.BooleanField(default=False) rule = models.ForeignKey( CollectionRule, on_delete=models.CASCADE, editable=False, related_name="posts" diff --git a/src/newsreader/news/core/serializers.py b/src/newsreader/news/core/serializers.py index d4353c9..c375dde 100644 --- a/src/newsreader/news/core/serializers.py +++ b/src/newsreader/news/core/serializers.py @@ -1,5 +1,6 @@ from rest_framework import serializers +from newsreader.news.collection.serializers import RuleSerializer from newsreader.news.core.models import Category, Post @@ -9,6 +10,8 @@ class PostSerializer(serializers.ModelSerializer): ) remoteIdentifier = serializers.CharField(source="remote_identifier", required=False) + rule = RuleSerializer(read_only=True) + class Meta: model = Post fields = ( @@ -19,6 +22,7 @@ class PostSerializer(serializers.ModelSerializer): "url", "rule", "read", + "saved", "publicationDate", "remoteIdentifier", ) 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 35fc741..78fc663 100644 --- a/src/newsreader/news/core/templates/news/core/views/categories.html +++ b/src/newsreader/news/core/templates/news/core/views/categories.html @@ -30,5 +30,9 @@ ] + {{ 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 6da166f..17a42d2 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,9 +1,13 @@ -{% extends "base.html" %} +{% extends "sidebar.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 1ec1487..31cd742 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,9 +1,13 @@ -{% extends "base.html" %} +{% extends "sidebar.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 79e1ccc..7e59f65 100644 --- a/src/newsreader/news/core/templates/news/core/views/homepage.html +++ b/src/newsreader/news/core/templates/news/core/views/homepage.html @@ -3,4 +3,11 @@ {% block content %}
    -{% endblock %} +{% endblock content %} + +{% block scripts %} + {{ homepageSettings|json_script:"homepageSettings" }} + {{ sidebar_links|json_script:"Links" }} + + {{ block.super }} +{% endblock scripts %} diff --git a/src/newsreader/news/core/templates/news/core/widgets/rule.html b/src/newsreader/news/core/templates/news/core/widgets/rule.html index beebe29..7ad5425 100644 --- a/src/newsreader/news/core/templates/news/core/widgets/rule.html +++ b/src/newsreader/news/core/templates/news/core/widgets/rule.html @@ -7,7 +7,7 @@ {% if option.instance.favicon %} {% else %} - + {% endif %} {{ option.label }} 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 1f42a20..f6d35f8 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"], "Not found.") + self.assertEquals(data["detail"], "No Category matches the given query.") 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 = [ + rules = FeedFactory.create_batch(size=5, category=category) + + for rule in 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,12 +164,10 @@ class CategoryReadTestCase(TestCase): self.client.logout() category = CategoryFactory(user=self.user) - rules = [ + rules = FeedFactory.create_batch(size=5, category=category, user=self.user) + + for rule in 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]) @@ -180,13 +178,10 @@ 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) - rules = [ + for rule in 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 15fb166..610dc58 100644 --- a/src/newsreader/news/core/tests/endpoints/category/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/category/list/tests.py @@ -1,12 +1,10 @@ import json -from datetime import date, datetime, time +from datetime import datetime, timezone 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 @@ -23,27 +21,21 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 3) def test_ordering(self): categories = [ CategoryFactory( - created=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc), user=self.user, ), CategoryFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc - ), + created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc), user=self.user, ), CategoryFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc), user=self.user, ), ] @@ -51,18 +43,18 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) - self.assertEquals(data[0]["id"], categories[1].pk) - self.assertEquals(data[1]["id"], categories[2].pk) - self.assertEquals(data[2]["id"], categories[0].pk) + self.assertEqual(data[0]["id"], categories[1].pk) + self.assertEqual(data[1]["id"], categories[2].pk) + self.assertEqual(data[2]["id"], categories[0].pk) def test_empty(self): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 0) def test_post(self): data = {"name": "Tech"} @@ -74,29 +66,29 @@ class CategoryListViewTestCase(TestCase): ) response_data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(response_data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(response_data["detail"], 'Method "POST" not allowed.') def test_patch(self): response = self.client.patch(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): response = self.client.put(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): response = self.client.delete(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_categories_with_unauthenticated_user(self): self.client.logout() @@ -105,7 +97,7 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_categories_with_unauthorized_user(self): other_user = UserFactory() @@ -114,8 +106,8 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 0) class NestedCategoryListViewTestCase(TestCase): @@ -125,15 +117,15 @@ class NestedCategoryListViewTestCase(TestCase): def test_simple(self): category = CategoryFactory.create(user=self.user) - rules = FeedFactory.create_batch(size=5, category=category) + FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 5) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 5) self.assertTrue("id" in data[0]) self.assertTrue("name" in data[0]) @@ -149,16 +141,16 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 0) - self.assertEquals(data, []) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 0) + self.assertEqual(data, []) def test_not_known(self): response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": 100}) ) - self.assertEquals(response.status_code, 404) + self.assertEqual(response.status_code, 404) def test_post(self): response = self.client.post( @@ -168,8 +160,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "POST" not allowed.') def test_patch(self): category = CategoryFactory.create(user=self.user) @@ -183,8 +175,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): category = CategoryFactory.create(user=self.user) @@ -198,8 +190,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): category = CategoryFactory.create(user=self.user) @@ -212,32 +204,32 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_with_unauthenticated_user(self): self.client.logout() category = CategoryFactory.create(user=self.user) - rules = FeedFactory.create_batch(size=5, category=category) + FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_with_unauthorized_user(self): other_user = UserFactory.create() category = CategoryFactory.create(user=other_user) - rules = FeedFactory.create_batch(size=5, category=category) + FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_ordering(self): category = CategoryFactory.create(user=self.user) @@ -252,12 +244,12 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 3) - self.assertEquals(data[0]["id"], rules[2].pk) - self.assertEquals(data[1]["id"], rules[0].pk) - self.assertEquals(data[2]["id"], rules[1].pk) + self.assertEqual(data[0]["id"], rules[2].pk) + self.assertEqual(data[1]["id"], rules[0].pk) + self.assertEqual(data[2]["id"], rules[1].pk) def test_only_rules_from_category_are_returned(self): other_category = CategoryFactory(user=self.user) @@ -275,12 +267,12 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 3) - self.assertEquals(data[0]["id"], rules[2].pk) - self.assertEquals(data[1]["id"], rules[0].pk) - self.assertEquals(data[2]["id"], rules[1].pk) + self.assertEqual(data[0]["id"], rules[2].pk) + self.assertEqual(data[1]["id"], rules[0].pk) + self.assertEqual(data[2]["id"], rules[1].pk) class NestedCategoryPostView(TestCase): @@ -290,27 +282,24 @@ class NestedCategoryPostView(TestCase): def test_simple(self): category = CategoryFactory.create(user=self.user) - rules = { - rule.pk: FeedPostFactory.create_batch(size=5, rule=rule) - for rule in FeedFactory.create_batch( - size=5, category=category, 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) response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) ) data = response.json() - posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 25) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 25) - self.assertTrue("id" in posts[0]) - self.assertTrue("title" in posts[0]) - self.assertTrue("body" in posts[0]) - self.assertTrue("rule" in posts[0]) - self.assertTrue("url" in posts[0]) + self.assertTrue("id" in data["results"][0]) + self.assertTrue("title" in data["results"][0]) + self.assertTrue("body" in data["results"][0]) + self.assertTrue("rule" in data["results"][0]) + self.assertTrue("url" in data["results"][0]) def test_no_rules(self): category = CategoryFactory.create(user=self.user) @@ -321,13 +310,13 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 0) - self.assertEquals(posts, []) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 0) + self.assertEqual(posts, []) def test_no_posts(self): category = CategoryFactory.create(user=self.user) - rules = FeedFactory.create_batch(size=5, user=self.user, category=category) + 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}) @@ -335,16 +324,16 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 0) - self.assertEquals(posts, []) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 0) + self.assertEqual(posts, []) def test_not_known(self): response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": 100}) ) - self.assertEquals(response.status_code, 404) + self.assertEqual(response.status_code, 404) def test_post(self): response = self.client.post( @@ -354,8 +343,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "POST" not allowed.') def test_patch(self): category = CategoryFactory.create(user=self.user) @@ -369,8 +358,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): category = CategoryFactory.create(user=self.user) @@ -384,8 +373,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): category = CategoryFactory.create(user=self.user) @@ -398,8 +387,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_with_unauthenticated_user(self): self.client.logout() @@ -410,7 +399,7 @@ class NestedCategoryPostView(TestCase): reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_with_unauthorized_user(self): other_user = UserFactory.create() @@ -420,7 +409,7 @@ class NestedCategoryPostView(TestCase): reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_ordering(self): category = CategoryFactory.create(user=self.user) @@ -437,52 +426,38 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second Reuters post", rule=reuters_rule, - publication_date=datetime.combine( - date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 21, 15, tzinfo=timezone.utc), ), FeedPostFactory.create( title="First Reuters post", rule=reuters_rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 20, 12, tzinfo=timezone.utc), ), ] - guardian_posts = [ - FeedPostFactory.create( - title="Second Guardian post", - rule=guardian_rule, - publication_date=datetime.combine( - date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc - ), - ), - FeedPostFactory.create( - title="First Guardian post", - rule=guardian_rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), - ), - ] + FeedPostFactory.create( + title="Second Guardian post", + rule=guardian_rule, + publication_date=datetime(2019, 5, 21, 14, tzinfo=timezone.utc), + ) - bbc_posts = [ - FeedPostFactory.create( - title="Second BBC post", - rule=bbc_rule, - publication_date=datetime.combine( - date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc - ), - ), - FeedPostFactory.create( - title="First BBC post", - rule=bbc_rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), 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), + ) response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) @@ -490,35 +465,31 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 6) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 6) - self.assertEquals(posts[0]["title"], "Second BBC post") - self.assertEquals(posts[1]["title"], "Second Reuters post") - self.assertEquals(posts[2]["title"], "Second Guardian post") + self.assertEqual(posts[0]["title"], "Second BBC post") + self.assertEqual(posts[1]["title"], "Second Reuters post") + self.assertEqual(posts[2]["title"], "Second Guardian post") - self.assertEquals(posts[3]["title"], "First BBC post") - self.assertEquals(posts[4]["title"], "First Reuters post") - self.assertEquals(posts[5]["title"], "First Guardian post") + self.assertEqual(posts[3]["title"], "First BBC post") + self.assertEqual(posts[4]["title"], "First Reuters post") + self.assertEqual(posts[5]["title"], "First Guardian post") def test_only_posts_from_category_are_returned(self): category = CategoryFactory.create(user=self.user) - other_category = CategoryFactory.create(user=self.user) + 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) - guardian_posts = [ - FeedPostFactory.create(rule=guardian_rule), - FeedPostFactory.create(rule=guardian_rule), - ] + FeedPostFactory.create(rule=guardian_rule) + FeedPostFactory.create(rule=guardian_rule) - other_posts = [ - FeedPostFactory.create(rule=other_rule), - FeedPostFactory.create(rule=other_rule), - ] + 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}) @@ -526,15 +497,15 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 2) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 2) - self.assertEquals(posts[0]["rule"], guardian_rule.pk) - self.assertEquals(posts[1]["rule"], guardian_rule.pk) + self.assertEqual(posts[0]["rule"]["id"], guardian_rule.pk) + self.assertEqual(posts[1]["rule"]["id"], guardian_rule.pk) - def test_unread_posts(self): + def test_posts(self): category = CategoryFactory.create(user=self.user) - rule = FeedFactory(category=category) + rule = FeedFactory(category=category, user=self.user) FeedPostFactory.create_batch(size=10, rule=rule, read=False) FeedPostFactory.create_batch(size=10, rule=rule, read=True) @@ -543,37 +514,13 @@ class NestedCategoryPostView(TestCase): reverse( "api:news:core:categories-nested-posts", kwargs={"pk": category.pk} ), - {"read": "false"}, ) data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) for post in posts: - self.assertEquals(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.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) - - for post in posts: - self.assertEquals(post["read"], True) + self.assertEqual(post["read"], False) 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 2d25a89..e9a9faa 100644 --- a/src/newsreader/news/core/tests/endpoints/post/detail/tests.py +++ b/src/newsreader/news/core/tests/endpoints/post/detail/tests.py @@ -22,8 +22,8 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["id"], post.pk) + self.assertEqual(response.status_code, 200) + self.assertEqual(data["id"], post.pk) self.assertTrue("title" in data) self.assertTrue("body" in data) @@ -37,8 +37,8 @@ class PostDetailViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:posts-detail", args=[100])) data = response.json() - self.assertEquals(response.status_code, 404) - self.assertEquals(data["detail"], "Not found.") + self.assertEqual(response.status_code, 404) + self.assertEqual(data["detail"], "No Post matches the given query.") def test_post(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -49,8 +49,8 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "POST" not allowed.') def test_patch(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -63,8 +63,8 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["title"], "This title is very accurate") + self.assertEqual(response.status_code, 200) + self.assertEqual(data["title"], "This title is very accurate") def test_identifier_cannot_be_changed(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -77,8 +77,8 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["id"], post.pk) + self.assertEqual(response.status_code, 200) + self.assertEqual(data["id"], post.pk) def test_rule_cannot_be_changed(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -98,7 +98,7 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) self.assertTrue(data["rule"], rule.pk) @@ -111,10 +111,11 @@ class PostDetailViewTestCase(TestCase): data=json.dumps({"title": "This title is very accurate"}), content_type="application/json", ) + data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["title"], "This title is very accurate") + self.assertEqual(response.status_code, 200) + self.assertEqual(data["title"], "This title is very accurate") def test_delete(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -125,8 +126,8 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_post_with_unauthenticated_user_without_category(self): self.client.logout() @@ -138,7 +139,7 @@ class PostDetailViewTestCase(TestCase): reverse("api:news:core:posts-detail", args=[post.pk]) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_post_with_unauthenticated_user_with_category(self): self.client.logout() @@ -150,7 +151,7 @@ class PostDetailViewTestCase(TestCase): reverse("api:news:core:posts-detail", args=[post.pk]) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_post_with_unauthorized_user_without_category(self): other_user = UserFactory() @@ -161,7 +162,7 @@ class PostDetailViewTestCase(TestCase): reverse("api:news:core:posts-detail", args=[post.pk]) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_post_with_unauthorized_user_with_category(self): other_user = UserFactory() @@ -172,7 +173,7 @@ class PostDetailViewTestCase(TestCase): reverse("api:news:core:posts-detail", args=[post.pk]) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_post_with_different_user_for_category_and_rule(self): other_user = UserFactory() @@ -183,7 +184,7 @@ class PostDetailViewTestCase(TestCase): reverse("api:news:core:posts-detail", args=[post.pk]) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_mark_read(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -196,8 +197,8 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["read"], True) + self.assertEqual(response.status_code, 200) + self.assertEqual(data["read"], True) def test_mark_unread(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -210,5 +211,33 @@ class PostDetailViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["read"], False) + self.assertEqual(response.status_code, 200) + self.assertEqual(data["read"], False) + + def test_mark_saved(self): + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule, saved=False) + + response = self.client.patch( + reverse("api:news:core:posts-detail", args=[post.pk]), + data=json.dumps({"saved": True}), + content_type="application/json", + ) + data = response.json() + + self.assertEqual(response.status_code, 200) + self.assertEqual(data["saved"], True) + + def test_mark_unsaved(self): + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule, saved=True) + + response = self.client.patch( + reverse("api:news:core:posts-detail", args=[post.pk]), + data=json.dumps({"saved": False}), + content_type="application/json", + ) + data = response.json() + + self.assertEqual(response.status_code, 200) + self.assertEqual(data["saved"], False) 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 3bf9d17..467fd05 100644 --- a/src/newsreader/news/core/tests/endpoints/post/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/post/list/tests.py @@ -1,10 +1,8 @@ -from datetime import date, datetime, time +from datetime import datetime, timezone 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 @@ -22,10 +20,8 @@ class PostListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 3) def test_ordering(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -34,201 +30,63 @@ class PostListViewTestCase(TestCase): FeedPostFactory( title="I'm the first post", rule=rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=timezone.utc), ), FeedPostFactory( title="I'm the second post", rule=rule, - publication_date=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc), ), FeedPostFactory( title="I'm the third post", rule=rule, - publication_date=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=timezone.utc), ), ] response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) + self.assertEqual(response.status_code, 200) - self.assertEquals(data["results"][0]["id"], posts[1].pk) - self.assertEquals(data["results"][1]["id"], posts[2].pk) - self.assertEquals(data["results"][2]["id"], posts[0].pk) + for index, post in enumerate(posts, start=0): + with self.subTest(post=post): + self.assertEqual(data["results"][index]["id"], post.pk) - def test_pagination_count(self): - rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - FeedPostFactory.create_batch(size=80, rule=rule) - page_size = 50 - - response = self.client.get(reverse("api:news:core:posts-list"), {"count": 50}) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 80) - self.assertEquals(len(data["results"]), page_size) - - def test_empty(self): - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) - - def test_post(self): - response = self.client.post(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') - - def test_patch(self): - response = self.client.patch(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') - - def test_put(self): - response = self.client.put(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') - - def test_delete(self): - response = self.client.delete(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') - - def test_posts_with_unauthenticated_user_without_category(self): - self.client.logout() - - FeedPostFactory.create_batch(size=3, rule=FeedFactory(user=self.user)) - - response = self.client.get(reverse("api:news:core:posts-list")) - - self.assertEquals(response.status_code, 403) - - def test_posts_with_unauthenticated_user_with_category(self): - self.client.logout() - - category = CategoryFactory(user=self.user) - - FeedPostFactory.create_batch( - size=3, rule=FeedFactory(user=self.user, category=category) - ) - - response = self.client.get(reverse("api:news:core:posts-list")) - - self.assertEquals(response.status_code, 403) - - def test_posts_with_unauthorized_user_without_category(self): - other_user = UserFactory() - - rule = FeedFactory(user=other_user, category=None) - FeedPostFactory.create_batch(size=3, rule=rule) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data["results"]), 0) - self.assertEquals(data["count"], 0) - - def test_posts_with_unauthorized_user_with_category(self): - other_user = UserFactory() - category = CategoryFactory(user=other_user) - - FeedPostFactory.create_batch( - size=3, rule=FeedFactory(user=other_user, category=category) - ) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data["results"]), 0) - self.assertEquals(data["count"], 0) - - # Note that this situation should not be possible, due to the user not being able - # to specify the user when creating categories/rules - def test_posts_with_authorized_rule_unauthorized_category(self): - other_user = UserFactory() - - rule = FeedFactory(user=self.user, category=CategoryFactory(user=other_user)) - FeedPostFactory.create_batch(size=3, rule=rule) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 0) - - def test_posts_with_authorized_user_without_category(self): - rule = FeedFactory(user=self.user, category=None) - FeedPostFactory.create_batch(size=3, rule=rule) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - def test_unread_posts(self): - rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - - FeedPostFactory.create_batch(size=10, rule=rule, read=False) - FeedPostFactory.create_batch(size=10, rule=rule, read=True) - - response = self.client.get( - reverse("api:news:core:posts-list"), {"read": "false"} - ) - - data = response.json() - posts = data["results"] - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) - - for post in posts: - self.assertEquals(post["read"], False) - - def test_read_posts(self): + def test_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")) + + data = response.json() + posts = data["results"] + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 20) + + for post in posts: + with self.subTest(post=post): + self.assertEqual(post["read"], False) + + def test_saved_posts(self): + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + + FeedPostFactory.create_batch(size=20, rule=rule, saved=False) + FeedPostFactory.create_batch(size=10, rule=rule, saved=True) + response = self.client.get( - reverse("api:news:core:posts-list"), {"read": "true"} + reverse("api:news:core:posts-list"), {"saved": "true"} ) data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) for post in posts: - self.assertEquals(post["read"], True) + with self.subTest(post=post): + self.assertEqual(post["saved"], True) diff --git a/src/newsreader/news/core/tests/factories.py b/src/newsreader/news/core/tests/factories.py index 520f940..88ce45e 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=pytz.utc) + publication_date = factory.Faker("date_time_this_year", tzinfo=timezone.utc) url = factory.Faker("url") remote_identifier = factory.Faker("uuid4") @@ -35,10 +35,3 @@ 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 2601b4a..d322dbb 100644 --- a/src/newsreader/news/core/tests/test_views.py +++ b/src/newsreader/news/core/tests/test_views.py @@ -55,9 +55,7 @@ class CategoryCreateViewTestCase(CategoryViewTestCase, TestCase): size=4, user=other_user, category=None ) - user_rules = CollectionRuleFactory.create_batch( - size=3, user=self.user, category=None - ) + 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 8096cf8..ea7e957 100644 --- a/src/newsreader/news/core/urls.py +++ b/src/newsreader/news/core/urls.py @@ -14,7 +14,6 @@ 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 9ef81eb..e7fc237 100644 --- a/src/newsreader/news/core/views.py +++ b/src/newsreader/news/core/views.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.urls import reverse_lazy from django.views.generic.base import TemplateView from django.views.generic.edit import CreateView, UpdateView @@ -6,31 +7,27 @@ 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(TemplateView): +class NewsView(NavListMixin, TemplateView): template_name = "news/core/views/homepage.html" - # TODO serialize objects to show filled main page def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - user = self.request.user - categories = { - category: category.rules.order_by("-created") - for category in user.categories.order_by("name") + return { + **context, + "homepageSettings": { + "feedUrl": reverse_lazy("news:collection:feed-update", args=(0,)), + "categoriesUrl": reverse_lazy("news:core:category-update", args=(0,)), + "timezone": settings.TIME_ZONE, + "autoMarking": self.request.user.auto_mark_read, + }, } - rules = { - rule: rule.posts.order_by("-publication_date")[:30] - for rule in user.rules.order_by("-created") - } - context.update(categories=categories, rules=rules) - return context - - -class CategoryViewMixin: +class CategoryViewMixin(NavListMixin): queryset = Category.objects.prefetch_related("rules").order_by("name") def get_queryset(self): @@ -58,6 +55,17 @@ class CategoryListView(CategoryViewMixin, ListView): template_name = "news/core/views/categories.html" context_object_name = "categories" + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + + return { + **context, + "categories_create_url": reverse_lazy("news:core:category-create"), + "categories_update_url": ( + reverse_lazy("news:core:category-update", args=(0,)) + ), + } + class CategoryUpdateView(CategoryViewMixin, CategoryDetailMixin, UpdateView): template_name = "news/core/views/category-update.html" diff --git a/src/newsreader/scss/components/body/_body.scss b/src/newsreader/scss/components/body/_body.scss index fed260b..964970a 100644 --- a/src/newsreader/scss/components/body/_body.scss +++ b/src/newsreader/scss/components/body/_body.scss @@ -2,8 +2,8 @@ margin: 0; padding: 0; - font-family: Rubik, sans-serif; - color: $font-color; + font-family: Inter; + font-size: $font-size; } body { diff --git a/src/newsreader/scss/components/card/_card.scss b/src/newsreader/scss/components/card/_card.scss index b77522a..96737a4 100644 --- a/src/newsreader/scss/components/card/_card.scss +++ b/src/newsreader/scss/components/card/_card.scss @@ -1,3 +1,6 @@ +@import '../../partials/variables'; +@import '../../lib/mixins'; + .card { display: flex; flex-direction: column; @@ -7,7 +10,11 @@ width: 50%; - background-color: $white; + background-color: var(--background-color); + + @media (max-width: $mobile-breakpoint) { + width: initial; + } &__header { display: flex; @@ -16,17 +23,17 @@ padding: 15px 0; - border-bottom: 2px $gray solid; + border-bottom: 2px var(--border-color) solid; } &__content { display: flex; - padding: 10px; + padding: 10px 0; } &__footer { display: flex; - padding: 10px; + padding: 10px 0; } & .favicon { diff --git a/src/newsreader/scss/components/category/_category.scss b/src/newsreader/scss/components/category/_category.scss deleted file mode 100644 index 6710af2..0000000 --- a/src/newsreader/scss/components/category/_category.scss +++ /dev/null @@ -1,41 +0,0 @@ -.category { - display: flex; - align-items: center; - - padding: 5px; - - &__info { - display: flex; - justify-content: space-between; - - width: 100%; - padding: 0 0 0 20px; - - overflow: hidden; - white-space: nowrap; - - - &:hover { - cursor: pointer; - } - } - - &__name { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - &__menu { - display: flex; - align-items: center; - - &:hover { - cursor: pointer; - } - } - - &--selected, &:hover { - background-color: $gray; - } -} diff --git a/src/newsreader/scss/components/category/index.scss b/src/newsreader/scss/components/category/index.scss deleted file mode 100644 index d434e4f..0000000 --- a/src/newsreader/scss/components/category/index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './category'; diff --git a/src/newsreader/scss/components/checkbox-list/_checkbox-list.scss b/src/newsreader/scss/components/checkbox-list/_checkbox-list.scss new file mode 100644 index 0000000..76b9b97 --- /dev/null +++ b/src/newsreader/scss/components/checkbox-list/_checkbox-list.scss @@ -0,0 +1,11 @@ +.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 new file mode 100644 index 0000000..3f7f471 --- /dev/null +++ b/src/newsreader/scss/components/checkbox-list/index.scss @@ -0,0 +1 @@ +@import './checkbox-list'; diff --git a/src/newsreader/scss/components/fieldset/_fieldset.scss b/src/newsreader/scss/components/fieldset/_fieldset.scss index c2588b5..8fd35b0 100644 --- a/src/newsreader/scss/components/fieldset/_fieldset.scss +++ b/src/newsreader/scss/components/fieldset/_fieldset.scss @@ -1,6 +1,8 @@ .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 9af3b12..513fec9 100644 --- a/src/newsreader/scss/components/form/_form.scss +++ b/src/newsreader/scss/components/form/_form.scss @@ -1,10 +1,16 @@ +@import '../../partials/variables'; + .form { display: flex; flex-direction: column; width: 70%; - background-color: $white; + background-color: var(--background-color); + + @media (max-width: $mobile-breakpoint) { + width: 100%; + } &__section { &--last { @@ -44,6 +50,7 @@ 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 44d4765..f917746 100644 --- a/src/newsreader/scss/components/form/_rules-form.scss +++ b/src/newsreader/scss/components/form/_rules-form.scss @@ -1,5 +1,21 @@ +@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/header/_header.scss b/src/newsreader/scss/components/header/_header.scss new file mode 100644 index 0000000..ed96dc6 --- /dev/null +++ b/src/newsreader/scss/components/header/_header.scss @@ -0,0 +1,3 @@ +.header { + padding: 15px; +} diff --git a/src/newsreader/scss/components/header/index.scss b/src/newsreader/scss/components/header/index.scss new file mode 100644 index 0000000..5c23e3e --- /dev/null +++ b/src/newsreader/scss/components/header/index.scss @@ -0,0 +1 @@ +@import './header'; diff --git a/src/newsreader/scss/components/index.scss b/src/newsreader/scss/components/index.scss index cc9e717..c8a933a 100644 --- a/src/newsreader/scss/components/index.scss +++ b/src/newsreader/scss/components/index.scss @@ -3,11 +3,13 @@ @import './main/index'; @import './navbar/index'; @import './loading-indicator/index'; +@import './theme-switcher/index'; @import './modal/index'; @import './card/index'; @import './list/index'; +@import './header/index'; @import './messages/index'; @import './section/index'; @import './errorlist/index'; @@ -17,9 +19,12 @@ @import './table/index'; @import './rules/index'; -@import './category/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/list/_list.scss b/src/newsreader/scss/components/list/_list.scss index 75e5e94..d90e26e 100644 --- a/src/newsreader/scss/components/list/_list.scss +++ b/src/newsreader/scss/components/list/_list.scss @@ -9,9 +9,7 @@ align-items: center; padding: 10px 0; - & > * { - margin: 0 15px; - } + gap: 15px; } } diff --git a/src/newsreader/scss/components/loading-indicator/_loading-indicator.scss b/src/newsreader/scss/components/loading-indicator/_loading-indicator.scss index 0651d1d..d28e87d 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: $lavendal-pink; + background-color: var(--font-color); 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 5d0143f..2990649 100644 --- a/src/newsreader/scss/components/main/_main.scss +++ b/src/newsreader/scss/components/main/_main.scss @@ -1,7 +1,22 @@ -.main { - display: flex; - flex-direction: column; - align-items: center; +@import '../../partials/variables'; - margin: 20px 0; +.main { + @media (max-width: $mobile-breakpoint) { + display: grid; + grid: [stack] 1fr / min-content [stack] 1fr; + + & .sidebar, .post-message, .posts, #{&}__container { + grid-area: stack; + } + } + + &__container { + display: flex; + flex-direction: column; + align-items: center; + + @media (max-width: $mobile-breakpoint) { + display: initial; + } + } } diff --git a/src/newsreader/scss/components/menu/_menu.scss b/src/newsreader/scss/components/menu/_menu.scss new file mode 100644 index 0000000..fe0025e --- /dev/null +++ b/src/newsreader/scss/components/menu/_menu.scss @@ -0,0 +1,26 @@ +@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 new file mode 100644 index 0000000..9cac78c --- /dev/null +++ b/src/newsreader/scss/components/menu/index.scss @@ -0,0 +1 @@ +@import './menu'; diff --git a/src/newsreader/scss/components/messages/_messages.scss b/src/newsreader/scss/components/messages/_messages.scss index 74d88b5..0e7a0a0 100644 --- a/src/newsreader/scss/components/messages/_messages.scss +++ b/src/newsreader/scss/components/messages/_messages.scss @@ -1,14 +1,15 @@ +@import '../../partials/variables'; +@import '../../partials/colors'; + .messages { display: flex; flex-direction: column; align-items: center; - position: fixed; - top: 0; width: 100%; margin: 5px 0 20px 0; - color: $white; + color: var(--font-color); &__item { width: 80%; @@ -17,7 +18,11 @@ padding: 20px 15px; margin: 5px 0; - background-color: $blue; + background-color: $transparant-blue; + + @media (max-width: $mobile-breakpoint) { + width: 90%; + } &--error { background-color: $transparant-red; @@ -27,16 +32,35 @@ background-color: $transparant-orange; } - // TODO check this color &--success { background-color: $transparant-green; } - & .gg-close { + & i { position: absolute; top: 15px; right: 15px; - --ggs: 2; } } + + &--fixed { + position: fixed; + top: 0; + } + + &--fixed &__item { + background-color: $blue; + } + + &--fixed &__item--error { + background-color: $red; + } + + &--fixed &__item--warning { + background-color: $orange; + } + + &--fixed &__item--success { + background-color: $green; + } } diff --git a/src/newsreader/scss/components/modal/_modal.scss b/src/newsreader/scss/components/modal/_modal.scss index 3ca246c..a0b764e 100644 --- a/src/newsreader/scss/components/modal/_modal.scss +++ b/src/newsreader/scss/components/modal/_modal.scss @@ -1,3 +1,5 @@ +@import '../../partials/variables'; + .modal { display: flex; flex-direction: column; @@ -8,7 +10,7 @@ height: 100%; top: 0; - background-color: $dark; + background-color: var(--background-color); &__item { display: flex; @@ -20,15 +22,17 @@ width: 60%; - background-color: $white; + @media (max-width: $mobile-breakpoint) { + width: initial; + } } &__header { - padding: 5px 20px; + padding: 5px 0; } &__content { - padding: 10px 30px; + padding: 10px 0; } &__footer { @@ -36,6 +40,6 @@ flex-direction: row; justify-content: space-between; - padding: 10px; + padding: 10px 0; } } diff --git a/src/newsreader/scss/components/modal/_post-modal.scss b/src/newsreader/scss/components/modal/_post-modal.scss index f6483fe..1a8d18f 100644 --- a/src/newsreader/scss/components/modal/_post-modal.scss +++ b/src/newsreader/scss/components/modal/_post-modal.scss @@ -1,3 +1,5 @@ +@import '../../partials/variables'; + .post-modal { @extend .modal; @@ -5,4 +7,10 @@ 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 new file mode 100644 index 0000000..f81ce94 --- /dev/null +++ b/src/newsreader/scss/components/nav-list/_nav-list.scss @@ -0,0 +1,16 @@ +.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 new file mode 100644 index 0000000..1201d0b --- /dev/null +++ b/src/newsreader/scss/components/nav-list/index.scss @@ -0,0 +1 @@ +@import './nav-list'; diff --git a/src/newsreader/scss/components/navbar/_navbar.scss b/src/newsreader/scss/components/navbar/_navbar.scss index 60c9f48..0fcf3a2 100644 --- a/src/newsreader/scss/components/navbar/_navbar.scss +++ b/src/newsreader/scss/components/navbar/_navbar.scss @@ -1,31 +1,50 @@ +@import '../../partials/variables'; +@import '../../lib/functions'; + .nav { display: flex; justify-content: center; + align-items: center; - margin: 0 0 5px 0; padding: 10px 0; width: 100%; + height: map-deep-get($nav, height); - ol { + background-color: var(--background-color); + + border-bottom: 2px var(--border-color) solid; + + @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 { display: flex; justify-content: flex-start; width: 80%; list-style-type: none; - } - &__item { - margin: 0px 10px; - - & a { - @extend .button; - - font-size: 18px !important; - font-weight: 600; + @media (max-width: $mobile-breakpoint) { + display: none; } } - &__item:last-child { - margin: 0 10px 0 auto; + & .nav-list { + width: 80%; + + @media (max-width: $mobile-breakpoint) { + display: none; + } + + &__item:last-child { + margin: 0 10px 0 auto; + + border-right: 2px solid var(--border-color); + } } } diff --git a/src/newsreader/scss/components/post-message/_post-message.scss b/src/newsreader/scss/components/post-message/_post-message.scss index 574d633..712ccd9 100644 --- a/src/newsreader/scss/components/post-message/_post-message.scss +++ b/src/newsreader/scss/components/post-message/_post-message.scss @@ -4,10 +4,8 @@ justify-content: center; align-items: center; - width: 60%; - height: 80vh; - - background-color: $white; + height: max-content; + margin: 20px 0 0 0; &__message { font-size: 16px; @@ -19,5 +17,9 @@ align-items: center; margin: 5px; + + & i { + padding: 0 $fa-padding 0 0; + } } } diff --git a/src/newsreader/scss/components/post/_post.scss b/src/newsreader/scss/components/post/_post.scss index e73dbd2..200d63f 100644 --- a/src/newsreader/scss/components/post/_post.scss +++ b/src/newsreader/scss/components/post/_post.scss @@ -1,39 +1,91 @@ +@import '../../partials/variables'; +@import '../../partials/colors'; +@import '../../lib/functions'; +@import '../../elements/button/'; + .post { display: flex; flex-direction: column; align-items: center; position: relative; - width: 80%; - height: 90%; + width: 35%; + + @media (max-width: $wqhd-breakpoint) { + width: 50%; + } + + @media (max-width: $mobile-breakpoint) { + width: 100%; + height: 100%; + + margin: 0; + } + + height: max-content; margin: 2% auto 5% auto; + padding: 0 0 20px 0; overflow-y: auto; - background-color: $white; + background-color: var(--background-color); + border-radius: 0.25em; cursor: initial; + + &__container { + width: 90%; + } + &__header { display: flex; flex-direction: column; - padding: 20px 0 10px 0; - width: 75%; + align-items: center; + + position: sticky; + top: 0; + + background-color: var(--background-color); } - &__title { - &--read { - color: $gainsboro; + &__actions { + display: flex; + justify-content: flex-end; + width: 100%; + + padding: 20px 0; + gap: 20px; + + @media (max-width: $mobile-breakpoint) { + justify-content: space-between; + flex-direction: row-reverse; + + gap: 0; } } - &__link { - display: inline-flex; - padding: 0 15px; + &__heading { + display: flex; + flex-direction: column; + padding: 20px 0 10px 0; - & img { - width: 30px; + @media (min-width: $hd-breakpoint) { + width: 80%; + } + + @media (max-width: $hd-breakpoint) { + width: 100%; + padding: 0; + } + } + + &__title { + font-size: map-deep-get($post, "header-font-size"); + + &--read { + text-decoration: line-through; } } @@ -46,10 +98,8 @@ } &__rule, &__category { - background-color: $orange !important; - & a { - color: $black; + color: var(--font-color); } } @@ -58,7 +108,15 @@ flex-direction: column; padding: 10px 0 30px 0; - width: 75%; + + @media (min-width: $hd-breakpoint) { + width: 72%; + } + + @media (max-width: $hd-breakpoint) { + width: 90%; + padding: 0; + } & p { padding: 10px 0; @@ -70,6 +128,8 @@ & img, video { padding: 10px 0; + + width: max-content; max-width: 100%; } @@ -79,23 +139,63 @@ } &__close-button { - position: relative; - margin: 1% 2% 0 0; - align-self: flex-end; - background-color: $blue; + background-color: var(--info-color); color: $white; - &:hover { - background-color: lighten($blue, +1%); + & i { + padding: 0 $fa-padding 0 0; } } - &__meta-info { + &__meta { display: flex; flex-direction: row; align-items: center; margin: 15px 0; - gap: 5px; + 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 c199961..ca76155 100644 --- a/src/newsreader/scss/components/posts-info/_posts-info.scss +++ b/src/newsreader/scss/components/posts-info/_posts-info.scss @@ -7,15 +7,27 @@ &__date { align-self: center; + + @media (max-width: $mobile-breakpoint){ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } - &__link { - display: inline-flex; + &__link, .saved-icon { + @media (max-width: $mobile-breakpoint){ + display: none; + } } & .badge { - & a { - color: $black; + & .link { + color: inherit; + } + + @media (max-width: $mobile-breakpoint){ + display: none; } } } diff --git a/src/newsreader/scss/components/posts/_posts.scss b/src/newsreader/scss/components/posts/_posts.scss index 8bb86e9..a8a89e6 100644 --- a/src/newsreader/scss/components/posts/_posts.scss +++ b/src/newsreader/scss/components/posts/_posts.scss @@ -1,15 +1,28 @@ +@import '../../partials/variables'; +@import '../../lib/functions'; + .posts { - width: 70%; - margin: 0 0 2% 20px; + height: calc(100vh - map-deep-get($nav, height)); + overflow-y: scroll; + padding: 0 0 0 10px; + + @media (max-width: $mobile-breakpoint) { + height: calc(100vh - map-deep-get($nav, mobile, height)); + padding: 0; + } &__list { display: flex; flex-direction: column; + width: 100%; + padding: 0; + list-style: none; - width: 95%; - padding: 0; + @media (max-width: $mobile-breakpoint) { + width: initial; + } } &__item { @@ -18,19 +31,24 @@ padding: 10px; + max-width: max-content; + + @media (max-width: $mobile-breakpoint) { + max-width: 100vw; + } + &:first-child { padding: 0 10px 10px 10px; } - & span { - font-size: small; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - & .badge { - background-color: $orange; + background-color: var(--background-color-secondary); + + @media (max-width: $mobile-breakpoint){ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } &:last-child { @@ -39,15 +57,13 @@ } &__header { - width: 80%; - overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - font-size: 16px; + font-size: map-deep-get($post, header-font-size); &--read { - color: darken($gainsboro, +10%); + text-decoration: line-through; } &:hover { diff --git a/src/newsreader/scss/components/rules/_rules.scss b/src/newsreader/scss/components/rules/_rules.scss index 527d99a..99ec039 100644 --- a/src/newsreader/scss/components/rules/_rules.scss +++ b/src/newsreader/scss/components/rules/_rules.scss @@ -1,3 +1,5 @@ +@import '../../partials/variables'; + .rules { padding: 0; @@ -6,19 +8,27 @@ 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: $gray; + background-color: var(--selected-color); } &--selected { - background-color: $gray; + background-color: var(--selected-color); } } @@ -27,8 +37,7 @@ align-items: center; width: 80%; - & .gg-image { - --ggs: 80%; + & i { margin: 0 5px 0 0; min-width: 20px; } 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 new file mode 100644 index 0000000..c745c62 --- /dev/null +++ b/src/newsreader/scss/components/scroll-to-top/_scroll-to-top.scss @@ -0,0 +1,39 @@ +@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 new file mode 100644 index 0000000..dcaf7de --- /dev/null +++ b/src/newsreader/scss/components/scroll-to-top/index.scss @@ -0,0 +1 @@ +@import './scroll-to-top'; diff --git a/src/newsreader/scss/components/sidebar/_sidebar.scss b/src/newsreader/scss/components/sidebar/_sidebar.scss index f13faf3..186a382 100644 --- a/src/newsreader/scss/components/sidebar/_sidebar.scss +++ b/src/newsreader/scss/components/sidebar/_sidebar.scss @@ -1,23 +1,156 @@ +@import '../../partials/variables'; +@import '../../lib/functions'; + .sidebar { - display: flex; - flex-direction: column; - align-items: center; - align-self: start; + display: none; // hide the sidebar by default, homepage enables it by default - position: sticky; - top: 5%; + --easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1); + --duration: .6s; - width: 20%; + 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); + } &__nav { - width: 100%; - max-height: 80vh; - overflow: auto; + display: flex; + flex-direction: column; + overflow-y: scroll; + background-color: var(--background-color); + + padding: 10px; + } + + &__close { + display: none; + + @media (max-width: $mobile-breakpoint) { + display: initial; + } + } + + &__list { list-style: none; + } - &__item { - padding: 2px 10px 5px 10px; + &__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); + } + } + + &__icon { + &:hover { + cursor: pointer; + } + } + + &__text { + display: flex; + justify-content: space-between; + + width: 100%; + padding: 0 0 0 20px; + + overflow: hidden; + white-space: nowrap; + + &:hover { + cursor: pointer; + } + } + + .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); + } +} diff --git a/src/newsreader/scss/components/table/_rules-table.scss b/src/newsreader/scss/components/table/_rules-table.scss index 3be0430..b558045 100644 --- a/src/newsreader/scss/components/table/_rules-table.scss +++ b/src/newsreader/scss/components/table/_rules-table.scss @@ -1,7 +1,17 @@ +@import '../../partials/variables'; + .rules-table { - &__heading { + padding: 15px; + + &__heading, &__item { + padding: 10px; + &--select { width: 5%; + + & .checkbox { + margin: 0; + } } &--name { @@ -10,10 +20,18 @@ &--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 74d5d6e..b8bc660 100644 --- a/src/newsreader/scss/components/table/_table.scss +++ b/src/newsreader/scss/components/table/_table.scss @@ -1,8 +1,8 @@ .table { table-layout: fixed; - background-color: $white; - width: 90%; + background-color: var(--background-color); + width: 100%; padding: 20px; text-align: left; @@ -10,6 +10,10 @@ &__heading { @extend .h1; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } &__row { @@ -27,7 +31,7 @@ } &__footer { - width: 80%; + width: 100%; padding: 10px 0; } } diff --git a/src/newsreader/scss/components/theme-switcher/_theme-switcher.scss b/src/newsreader/scss/components/theme-switcher/_theme-switcher.scss new file mode 100644 index 0000000..78959f2 --- /dev/null +++ b/src/newsreader/scss/components/theme-switcher/_theme-switcher.scss @@ -0,0 +1,5 @@ +.theme-switcher { + &:hover { + cursor: pointer; + } +} diff --git a/src/newsreader/scss/components/theme-switcher/index.scss b/src/newsreader/scss/components/theme-switcher/index.scss new file mode 100644 index 0000000..0037786 --- /dev/null +++ b/src/newsreader/scss/components/theme-switcher/index.scss @@ -0,0 +1 @@ +@import './theme-switcher'; diff --git a/src/newsreader/scss/elements/badge/_badge.scss b/src/newsreader/scss/elements/badge/_badge.scss index efdd1b7..3842d45 100644 --- a/src/newsreader/scss/elements/badge/_badge.scss +++ b/src/newsreader/scss/elements/badge/_badge.scss @@ -1,3 +1,6 @@ +@import '../../partials/variables'; +@import '../../partials/colors'; + .badge { display: inline-block; @@ -6,7 +9,12 @@ text-align: center; - background-color: $gainsboro; + background-color: var(--background-color-secondary); 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 a8eb3bc..237e37d 100644 --- a/src/newsreader/scss/elements/button/_button.scss +++ b/src/newsreader/scss/elements/button/_button.scss @@ -1,4 +1,4 @@ -.button { +@mixin button { display: flex; align-items: center; @@ -7,47 +7,40 @@ @include button-padding; border: none; - - font-size: 16px; + border-radius: 0.25em; &:hover { cursor: pointer; } - &--success, &--confirm { + &--success, + &--confirm { + background-color: var(--confirm-color); color: $white !important; - background-color: $green; - - &:hover { - background-color: lighten($green, +5%); - } } - &--error, &--cancel { + &--error, + &--cancel { color: $white !important; - background-color: $red; - - &:hover { - background-color: lighten($red, +5%); - } - + background-color: var(--danger-color); } &--primary { color: $white !important; - background-color: $blue; + background-color: var(--info-color); + } + + &--disabled { + color: var(--font-color) !important; + background-color: var(--background-color-secondary) !important; &:hover { - background-color: lighten($blue, 5%); + cursor: default; } } - &--reddit { - color: $white !important; - background-color: lighten($reddit-orange, 5%); - - &:hover { - background-color: $reddit-orange; - } - } +} + +.button { + @include button; } diff --git a/src/newsreader/scss/elements/button/_read-button.scss b/src/newsreader/scss/elements/button/_read-button.scss index 2c345b5..bd09cbf 100644 --- a/src/newsreader/scss/elements/button/_read-button.scss +++ b/src/newsreader/scss/elements/button/_read-button.scss @@ -1,12 +1,18 @@ +@import '../../partials/variables'; +@import '../../partials/colors'; + .read-button { @extend .button; - margin: 20px 0 0 0; + color: var(--confirm-font-color); - color: $white; - background-color: $green; + background-color: var(--confirm-color); - &:hover { - background-color: darken($green, 10%); + & 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 f23a7c3..b818723 100644 --- a/src/newsreader/scss/elements/checkbox/_checkbox.scss +++ b/src/newsreader/scss/elements/checkbox/_checkbox.scss @@ -2,8 +2,6 @@ display: block; height: 20px; width: 20px; - margin: 0 0 0 20px; - & input[type=checkbox] { position: absolute; @@ -14,7 +12,7 @@ &:checked + .checkbox__label { .checkbox__box { - background-color: $checkbox-blue; + background-color: var(--info-color); } } } @@ -29,7 +27,7 @@ height: 100%; width: 100%; - border: 2px solid darken($gainsboro, 10%); + border: 1.5px solid var(--border-color); cursor: pointer; } } diff --git a/src/newsreader/scss/elements/h1/_h1.scss b/src/newsreader/scss/elements/h1/_h1.scss index 6ddee05..ce168c8 100644 --- a/src/newsreader/scss/elements/h1/_h1.scss +++ b/src/newsreader/scss/elements/h1/_h1.scss @@ -1,5 +1,5 @@ .h1 { - color: $header-color; + color: var(--font-color); font-size: 20px; } diff --git a/src/newsreader/scss/elements/h2/_h2.scss b/src/newsreader/scss/elements/h2/_h2.scss index 9f77ad8..41cd57c 100644 --- a/src/newsreader/scss/elements/h2/_h2.scss +++ b/src/newsreader/scss/elements/h2/_h2.scss @@ -1,5 +1,5 @@ .h2 { - color: $header-color; + color: var(--font-color); } h2 { diff --git a/src/newsreader/scss/elements/h3/_h3.scss b/src/newsreader/scss/elements/h3/_h3.scss index 7394bd7..89fdaf9 100644 --- a/src/newsreader/scss/elements/h3/_h3.scss +++ b/src/newsreader/scss/elements/h3/_h3.scss @@ -1,5 +1,5 @@ .h3 { - color: $header-color; + color: var(--font-color); } h3 { diff --git a/src/newsreader/scss/elements/h4/_h4.scss b/src/newsreader/scss/elements/h4/_h4.scss index ab24888..1ff0da6 100644 --- a/src/newsreader/scss/elements/h4/_h4.scss +++ b/src/newsreader/scss/elements/h4/_h4.scss @@ -1,5 +1,5 @@ .h4 { - color: $header-color; + color: var(--font-color); } h4 { diff --git a/src/newsreader/scss/elements/h5/_h5.scss b/src/newsreader/scss/elements/h5/_h5.scss index 5efa58e..4791e71 100644 --- a/src/newsreader/scss/elements/h5/_h5.scss +++ b/src/newsreader/scss/elements/h5/_h5.scss @@ -1,5 +1,5 @@ .h5 { - color: $header-color; + color: var(--font-color); } h5 { diff --git a/src/newsreader/scss/elements/help-text/_help-text.scss b/src/newsreader/scss/elements/help-text/_help-text.scss index a90552d..d91adc0 100644 --- a/src/newsreader/scss/elements/help-text/_help-text.scss +++ b/src/newsreader/scss/elements/help-text/_help-text.scss @@ -1,7 +1,8 @@ .help-text { @extend .small; - padding: 5px 15px; + padding: 10px 0; + } .helptext { diff --git a/src/newsreader/scss/elements/index.scss b/src/newsreader/scss/elements/index.scss index 0c30aff..718b562 100644 --- a/src/newsreader/scss/elements/index.scss +++ b/src/newsreader/scss/elements/index.scss @@ -12,3 +12,4 @@ @import './small/index'; @import './select/index'; @import './checkbox/index'; +@import './saved-icon/index'; diff --git a/src/newsreader/scss/elements/input/_input.scss b/src/newsreader/scss/elements/input/_input.scss index 16e6fad..fd7a231 100644 --- a/src/newsreader/scss/elements/input/_input.scss +++ b/src/newsreader/scss/elements/input/_input.scss @@ -1,11 +1,9 @@ .input { @include text-padding; - border: 1px $gray solid; - - &:focus { - border: 1px $focus-blue solid; - } + color: var(--font-color); + background-color: var(--background-color-secondary); + border: 1px var(--border-color) solid; &[type="file"] { width: 40%; @@ -13,7 +11,6 @@ &[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 6481b02..abf59a2 100644 --- a/src/newsreader/scss/elements/label/_label.scss +++ b/src/newsreader/scss/elements/label/_label.scss @@ -1,5 +1,5 @@ .label { - @include text-padding; + padding: 10px 0; } label { diff --git a/src/newsreader/scss/elements/link/_link.scss b/src/newsreader/scss/elements/link/_link.scss index b485cb3..dc4ac42 100644 --- a/src/newsreader/scss/elements/link/_link.scss +++ b/src/newsreader/scss/elements/link/_link.scss @@ -1,5 +1,5 @@ .link { - color: darken($azureish-white, 30%); + color: var(--link-color); text-decoration: none; &:hover { diff --git a/src/newsreader/scss/elements/saved-icon/_saved-icon.scss b/src/newsreader/scss/elements/saved-icon/_saved-icon.scss new file mode 100644 index 0000000..21fea31 --- /dev/null +++ b/src/newsreader/scss/elements/saved-icon/_saved-icon.scss @@ -0,0 +1,15 @@ +.saved-icon { + @include font-awesome; + + &:before { + content: "\f0c7"; + } + + &:hover { + cursor: pointer; + } + + &--saved { + color: var(--confirm-color); + } +} diff --git a/src/newsreader/scss/elements/saved-icon/index.scss b/src/newsreader/scss/elements/saved-icon/index.scss new file mode 100644 index 0000000..db05603 --- /dev/null +++ b/src/newsreader/scss/elements/saved-icon/index.scss @@ -0,0 +1 @@ +@import './saved-icon'; diff --git a/src/newsreader/scss/elements/small/_small.scss b/src/newsreader/scss/elements/small/_small.scss index c95bfab..9b28e4e 100644 --- a/src/newsreader/scss/elements/small/_small.scss +++ b/src/newsreader/scss/elements/small/_small.scss @@ -1,5 +1,5 @@ .small { - color: $nickel; + color: var(--font-color); font-size: small; } diff --git a/src/newsreader/scss/lib/_css.gg.scss b/src/newsreader/scss/lib/_css.gg.scss deleted file mode 100644 index a1b1338..0000000 --- a/src/newsreader/scss/lib/_css.gg.scss +++ /dev/null @@ -1,10 +0,0 @@ -@import '~css.gg/icons-scss/icons'; - -.gg-link { - color: initial; -} - -.gg-pen { - transform: rotate(-45deg) scale(var(--ggs, 0.8)); - color: initial; -} diff --git a/src/newsreader/scss/lib/_font-awesome.scss b/src/newsreader/scss/lib/_font-awesome.scss new file mode 100644 index 0000000..b422bd6 --- /dev/null +++ b/src/newsreader/scss/lib/_font-awesome.scss @@ -0,0 +1,5 @@ +$fa-font-path: '~@fortawesome/fontawesome-free/webfonts'; + +@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 new file mode 100644 index 0000000..cb8189f --- /dev/null +++ b/src/newsreader/scss/lib/_functions.scss @@ -0,0 +1,7 @@ +@function map-deep-get($map, $keys...) { + @each $key in $keys { + $map: map-get($map, $key); + } + + @return $map; +} diff --git a/src/newsreader/scss/lib/_mixins.scss b/src/newsreader/scss/lib/_mixins.scss index 72c9932..4667660 100644 --- a/src/newsreader/scss/lib/_mixins.scss +++ b/src/newsreader/scss/lib/_mixins.scss @@ -7,5 +7,10 @@ } @mixin button-padding { - padding: 7px 40px; + padding: 5px 20px; +} + +@mixin font-awesome { + font-family: "Font Awesome 5 Free"; + font-weight: 900; } diff --git a/src/newsreader/scss/lib/index.scss b/src/newsreader/scss/lib/index.scss index 026bf87..4bf4dc5 100644 --- a/src/newsreader/scss/lib/index.scss +++ b/src/newsreader/scss/lib/index.scss @@ -1,2 +1,2 @@ -@import 'css.gg'; +@import 'font-awesome'; @import 'mixins'; diff --git a/src/newsreader/scss/pages/homepage/index.scss b/src/newsreader/scss/pages/homepage/index.scss index 30f5a50..99260a7 100644 --- a/src/newsreader/scss/pages/homepage/index.scss +++ b/src/newsreader/scss/pages/homepage/index.scss @@ -1,9 +1,26 @@ #homepage--page { - display: flex; - flex-direction: row; - align-items: initial; - width: 100%; + background-color: initial; - margin: 20px 0 0 0; - background-color: initial; + 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; + } } diff --git a/src/newsreader/scss/pages/login/index.scss b/src/newsreader/scss/pages/login/index.scss index f1805ed..68ac32d 100644 --- a/src/newsreader/scss/pages/login/index.scss +++ b/src/newsreader/scss/pages/login/index.scss @@ -1,11 +1,24 @@ #login--page { - margin: 5% auto; - width: 50%; - & .form { @extend .form; - width: 100%; + 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%; + } h4 { margin: 0; @@ -21,9 +34,6 @@ &__fieldset { @extend .form__fieldset; - - &--last { - } } } } diff --git a/src/newsreader/scss/pages/rules/index.scss b/src/newsreader/scss/pages/rules/index.scss index 64f46b4..de69b2d 100644 --- a/src/newsreader/scss/pages/rules/index.scss +++ b/src/newsreader/scss/pages/rules/index.scss @@ -1,5 +1,3 @@ #rules--page { - & .table { - width: 100%; - } + // TODO: remove scss } diff --git a/src/newsreader/scss/pages/settings/index.scss b/src/newsreader/scss/pages/settings/index.scss index c52f46b..8f1e57a 100644 --- a/src/newsreader/scss/pages/settings/index.scss +++ b/src/newsreader/scss/pages/settings/index.scss @@ -3,6 +3,7 @@ &__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 b2f124d..d2433f6 100644 --- a/src/newsreader/scss/partials/_colors.scss +++ b/src/newsreader/scss/partials/_colors.scss @@ -1,27 +1,61 @@ -$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); +$orange: #ff2a51; +$green: #007936; +$red: #d30038; +$blue: #0085f2; -$white: rgba(255, 255, 255, 1); -$black: rgba(0, 0, 0, 1); -$dark: rgba(0, 0, 0, 0.4); - -$font-color: rgba(48, 51, 53, 1); -$header-color: rgba(100, 101, 102, 1); - -$reddit-orange: rgba(255, 69, 0, 1); +$white: #fff; +$black: #000; $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); +// White theme +$background-color: $white; +$background-color-secondary: #f9f9fb; -$focus-blue: darken($azureish-white, +10%); -$checkbox-blue: rgba(34, 170, 253, 1); +$font-color: #1b1b1b; + +$link-color: #0069c2; +$selected-color: #0085f230; +$read-color: darken($font-color, 10%); + +$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; + +// Dark theme +$dark-background-color: #1b1b1b; +$dark-background-color-secondary: #313131; + +$dark-font-color: #cccccc; + +$dark-link-color: $link-color; +$dark-read-color: darken($dark-font-color, 5%); + +$dark-confirm-color: $green; +$dark-confirm-font-color: $white; + +$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; diff --git a/src/newsreader/scss/partials/_fonts.scss b/src/newsreader/scss/partials/_fonts.scss index bcceb13..5b07f26 100644 --- a/src/newsreader/scss/partials/_fonts.scss +++ b/src/newsreader/scss/partials/_fonts.scss @@ -1,10 +1,46 @@ @font-face { - font-family: Rubik; - src: url('../assets/fonts/Rubik-Regular.ttf'); + font-family: Inter; + font-style: normal; + font-display: swap; + src: url('../assets/fonts/Inter-VariableFont_opsz,wght.ttf'); } @font-face { - font-family: Rubik; - src: url('../assets/fonts/Rubik-Bold.ttf'); - font-weight: bold; + font-family: Inter; + font-style: italic; + font-display: swap; + src: url('../assets/fonts/Inter-Italic-VariableFont_opsz,wght.ttf'); +} + +@font-face { + font-family: "Font Awesome 5 Free"; + src: url('~@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf'), + url('~@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff'), + url('~@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2'); + font-weight: bold; + style: normal; + weight: 900; + stretch: 100; +} + +@font-face { + font-family: "Font Awesome 5 Free"; + src: url('~@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf'), + url('~@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff'), + url('~@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2'); + font-weight: bold; + style: normal; + weight: 900; + stretch: 100; +} + +@font-face { + font-family: "Font Awesome 5 Free"; + src: url('~@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf'), + url('~@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff'), + url('~@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2'); + font-weight: bold; + style: normal; + weight: 900; + stretch: 100; } diff --git a/src/newsreader/scss/partials/_root.scss b/src/newsreader/scss/partials/_root.scss new file mode 100644 index 0000000..afc9617 --- /dev/null +++ b/src/newsreader/scss/partials/_root.scss @@ -0,0 +1,40 @@ +:root { + --background-color: #{$background-color}; + --background-color-secondary: #{$background-color-secondary}; + + --font-color: #{$font-color}; + --link-color: #{$link-color}; + --selected-color: #{$selected-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}; + + --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}; + } + + color: var(--font-color); + background-color: var(--background-color); +} diff --git a/src/newsreader/scss/partials/_variables.scss b/src/newsreader/scss/partials/_variables.scss new file mode 100644 index 0000000..b4165d3 --- /dev/null +++ b/src/newsreader/scss/partials/_variables.scss @@ -0,0 +1,36 @@ +$fa-padding: 7px; + +// Fonts +$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/scss/partials/index.scss b/src/newsreader/scss/partials/index.scss index ff28d1b..2a4d507 100644 --- a/src/newsreader/scss/partials/index.scss +++ b/src/newsreader/scss/partials/index.scss @@ -1,2 +1,4 @@ @import './colors'; @import './fonts'; +@import './root'; +@import './variables'; diff --git a/src/newsreader/templates/base.html b/src/newsreader/templates/base.html index 3f677c0..9900b37 100644 --- a/src/newsreader/templates/base.html +++ b/src/newsreader/templates/base.html @@ -4,6 +4,7 @@ Newreader + {% block head %} @@ -11,22 +12,16 @@ + + {% if messages %} diff --git a/src/newsreader/templates/components/form/form.html b/src/newsreader/templates/components/form/form.html index e183c25..9f1ab47 100644 --- a/src/newsreader/templates/components/form/form.html +++ b/src/newsreader/templates/components/form/form.html @@ -4,7 +4,7 @@ {% csrf_token %} {% if title %} - {% include "components/form/title.html" with title=title only %} + {% include "components/header/header.html" with title=title only %} {% endif %} {% block intro %} diff --git a/src/newsreader/templates/components/form/title.html b/src/newsreader/templates/components/form/title.html deleted file mode 100644 index 3adcb75..0000000 --- a/src/newsreader/templates/components/form/title.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -

    {{ title }}

    -
    diff --git a/src/newsreader/templates/components/header/header.html b/src/newsreader/templates/components/header/header.html new file mode 100644 index 0000000..c21c233 --- /dev/null +++ b/src/newsreader/templates/components/header/header.html @@ -0,0 +1,3 @@ +
    +

    {{ title }}

    +
    diff --git a/src/newsreader/templates/components/nav-list/nav-list.html b/src/newsreader/templates/components/nav-list/nav-list.html new file mode 100644 index 0000000..1c76122 --- /dev/null +++ b/src/newsreader/templates/components/nav-list/nav-list.html @@ -0,0 +1,16 @@ + diff --git a/src/newsreader/templates/password-reset/password-reset-complete.html b/src/newsreader/templates/password-reset/password-reset-complete.html index 0b7796f..ddd41e7 100755 --- a/src/newsreader/templates/password-reset/password-reset-complete.html +++ b/src/newsreader/templates/password-reset/password-reset-complete.html @@ -1,13 +1,15 @@ -{% extends "base.html" %} +{% extends "sidebar.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 d0d5037..852283c 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 "base.html" %} +{% extends "sidebar.html" %} {% load i18n %} {% block meta %} @@ -8,23 +8,25 @@ {% 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 7012439..b160339 100755 --- a/src/newsreader/templates/password-reset/password-reset-done.html +++ b/src/newsreader/templates/password-reset/password-reset-done.html @@ -1,16 +1,18 @@ -{% extends "base.html" %} +{% extends "sidebar.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 97e5678..0454f4e 100644 --- a/src/newsreader/templates/password-reset/password-reset.html +++ b/src/newsreader/templates/password-reset/password-reset.html @@ -1,7 +1,9 @@ -{% extends "base.html" %} +{% extends "sidebar.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 deleted file mode 100755 index f8dd91b..0000000 --- a/src/newsreader/templates/registration/activation_complete.html +++ /dev/null @@ -1,24 +0,0 @@ -{% 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 deleted file mode 100644 index 8773b29..0000000 --- a/src/newsreader/templates/registration/activation_email.html +++ /dev/null @@ -1,72 +0,0 @@ -{% 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 deleted file mode 100644 index d07e785..0000000 --- a/src/newsreader/templates/registration/activation_email.txt +++ /dev/null @@ -1,52 +0,0 @@ -{% 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 deleted file mode 100644 index da0ddeb..0000000 --- a/src/newsreader/templates/registration/activation_email_subject.txt +++ /dev/null @@ -1,28 +0,0 @@ -{% 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 deleted file mode 100644 index c99cc34..0000000 --- a/src/newsreader/templates/registration/activation_failure.html +++ /dev/null @@ -1,19 +0,0 @@ -{% 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 deleted file mode 100644 index 6d01fee..0000000 --- a/src/newsreader/templates/registration/activation_resend_complete.html +++ /dev/null @@ -1,23 +0,0 @@ -{% 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 deleted file mode 100644 index 5f0dd82..0000000 --- a/src/newsreader/templates/registration/activation_resend_form.html +++ /dev/null @@ -1,9 +0,0 @@ -{% 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 deleted file mode 100755 index c7cfd9a..0000000 --- a/src/newsreader/templates/registration/registration_closed.html +++ /dev/null @@ -1,10 +0,0 @@ -{% 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 deleted file mode 100755 index ccf70b2..0000000 --- a/src/newsreader/templates/registration/registration_complete.html +++ /dev/null @@ -1,19 +0,0 @@ -{% 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 deleted file mode 100644 index ccc07c9..0000000 --- a/src/newsreader/templates/registration/registration_form.html +++ /dev/null @@ -1,9 +0,0 @@ -{% 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 new file mode 100644 index 0000000..bf99634 --- /dev/null +++ b/src/newsreader/templates/sidebar.html @@ -0,0 +1,7 @@ +{% 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 0779b29..2e4c9c3 100644 --- a/src/newsreader/urls.py +++ b/src/newsreader/urls.py @@ -3,9 +3,6 @@ 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 @@ -14,16 +11,12 @@ 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/apps.py b/src/newsreader/utils/apps.py index 3e82e49..b2a1c2c 100644 --- a/src/newsreader/utils/apps.py +++ b/src/newsreader/utils/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class UtilsConfig(AppConfig): - name = "utils" + name = "newsreader.utils" diff --git a/src/newsreader/utils/celery.py b/src/newsreader/utils/celery.py index 84572c6..4a03e71 100644 --- a/src/newsreader/utils/celery.py +++ b/src/newsreader/utils/celery.py @@ -1,6 +1,6 @@ -from django.core.cache import cache +from time import monotonic -from celery.five import monotonic +from django.core.cache import cache LOCK_EXPIRE = 60 * 10 # 10 minutes diff --git a/src/newsreader/utils/opml.py b/src/newsreader/utils/opml.py index 55a9387..7657a03 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 not "xmlUrl" in element.keys(): + if "xmlUrl" not in element.keys(): continue feed_url = element.get("xmlUrl") @@ -38,4 +38,5 @@ def parse_opml(file, user, skip_existing=False): logging.info(f"Skipped due to invalid URL: {e}") continue + # TODO create feed type rules yield CollectionRule(url=feed_url, name=name, user=user) diff --git a/src/newsreader/utils/views.py b/src/newsreader/utils/views.py index 60f00ef..8325aa5 100644 --- a/src/newsreader/utils/views.py +++ b/src/newsreader/utils/views.py @@ -1 +1,29 @@ -# Create your views here. +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} diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..d357490 --- /dev/null +++ b/uv.lock @@ -0,0 +1,813 @@ +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 4ad1700..7cc970c 100644 --- a/webpack.common.babel.js +++ b/webpack.common.babel.js @@ -3,6 +3,7 @@ import { CleanWebpackPlugin } from 'clean-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; export default { + resolve: { extensions: ['.js', '.scss'] }, entry: { main: ['./src/newsreader/js/index.js', './src/newsreader/scss/index.scss'], }, @@ -22,24 +23,30 @@ export default { use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader', 'sass-loader'], }, { - test: /\.(ttf|woff|woff2)$/, - use: { - loader: 'file-loader', - options: { - name: 'fonts/[name].[ext]', - publicPath: '../', - }, + 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/', }, }, ], }, plugins: [ - new MiniCssExtractPlugin({ - filename: 'css/main.css', - allChunks: true, - }), + new MiniCssExtractPlugin({ filename: 'css/main.css' }), new CleanWebpackPlugin({ - cleanOnceBeforeBuildPatterns: ['**/*', '!favicon.png'], + cleanOnceBeforeBuildPatterns: ['js', 'css', 'fonts'], + cleanAfterEveryBuildPatterns: ['!fonts/**'], }), ], };