From e680baec00bc8efaa06725c9e15845fbcff7c1bf Mon Sep 17 00:00:00 2001 From: mavel Date: Tue, 7 Mar 2023 17:11:45 +0300 Subject: [PATCH 01/67] change requirements to allow running on Python 3.10 --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2d1e5b749..318fee174 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ django==3.2.15 django-debug-toolbar==3.2.1 -Pillow==8.2.0 +Pillow==9.4.0 environs[django]==9.3.2 +wheel==0.38.4 From 93556699e26029367c96b04fe0808db2322fde7f Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 9 Mar 2023 22:14:07 +0300 Subject: [PATCH 02/67] create models for orders, set up DRF --- .gitignore | 1 + foodcartapp/admin.py | 12 + .../migrations/0038_auto_20230309_1702.py | 44 + foodcartapp/models.py | 47 + foodcartapp/views.py | 22 +- package-lock.json | 3135 +---------------- package.json | 1 + requirements.txt | 2 + star_burger/settings.py | 5 + star_burger/urls.py | 1 + 10 files changed, 166 insertions(+), 3104 deletions(-) create mode 100644 foodcartapp/migrations/0038_auto_20230309_1702.py diff --git a/.gitignore b/.gitignore index 0ad052d36..9447fd3c4 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,4 @@ dmypy.json cython_debug/ .parcel-cache/ +/data/ diff --git a/foodcartapp/admin.py b/foodcartapp/admin.py index b31edd3b2..ca5b3b955 100644 --- a/foodcartapp/admin.py +++ b/foodcartapp/admin.py @@ -7,6 +7,8 @@ from .models import ProductCategory from .models import Restaurant from .models import RestaurantMenuItem +from .models import Order +from .models import ProductOrder class RestaurantMenuItemInline(admin.TabularInline): @@ -104,3 +106,13 @@ def get_image_list_preview(self, obj): @admin.register(ProductCategory) class ProductAdmin(admin.ModelAdmin): pass + + +class ProductOrderInline(admin.TabularInline): + model = ProductOrder + + +@admin.register(Order) +class OrderAdmin(admin.ModelAdmin): + inlines = [ProductOrderInline] + list_display = ('firstname', 'lastname', 'phonenumber', 'created_at') diff --git a/foodcartapp/migrations/0038_auto_20230309_1702.py b/foodcartapp/migrations/0038_auto_20230309_1702.py new file mode 100644 index 000000000..310607775 --- /dev/null +++ b/foodcartapp/migrations/0038_auto_20230309_1702.py @@ -0,0 +1,44 @@ +# Generated by Django 3.2.15 on 2023-03-09 17:02 + +from django.db import migrations, models +import django.db.models.deletion +import phonenumber_field.modelfields + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0037_auto_20210125_1833'), + ] + + operations = [ + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('firstname', models.CharField(max_length=50, verbose_name='имя')), + ('lastname', models.CharField(db_index=True, max_length=50, verbose_name='фамилия')), + ('phonenumber', phonenumber_field.modelfields.PhoneNumberField(db_index=True, max_length=128, region=None, verbose_name='телефон')), + ('address', models.CharField(max_length=200, verbose_name='адрес')), + ('created_at', models.DateTimeField(auto_now=True, verbose_name='время создания')), + ], + options={ + 'verbose_name': 'заказ', + 'verbose_name_plural': 'заказы', + }, + ), + migrations.CreateModel( + name='ProductOrder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.IntegerField(verbose_name='количество')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='foodcartapp.order')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='foodcartapp.product')), + ], + ), + migrations.AddField( + model_name='order', + name='products', + field=models.ManyToManyField(related_name='order', through='foodcartapp.ProductOrder', to='foodcartapp.Product', verbose_name='товары'), + ), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index 803492d33..77f367bce 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -1,5 +1,6 @@ from django.db import models from django.core.validators import MinValueValidator +from phonenumber_field.modelfields import PhoneNumberField class Restaurant(models.Model): @@ -121,3 +122,49 @@ class Meta: def __str__(self): return f"{self.restaurant.name} - {self.product.name}" + + +class ProductOrder(models.Model): + product = models.ForeignKey('Product', on_delete=models.CASCADE) + order = models.ForeignKey('Order', on_delete=models.CASCADE) + quantity = models.IntegerField('количество') + + def __str__(self): + return f'{self.product}, {self.quantity}' + + +class Order(models.Model): + firstname = models.CharField( + 'имя', + max_length=50, + ) + lastname = models.CharField( + 'фамилия', + max_length=50, + db_index=True, + ) + phonenumber = PhoneNumberField( + 'телефон', + db_index=True, + ) + address = models.CharField( + 'адрес', + max_length=200, + ) + products = models.ManyToManyField( + 'Product', + verbose_name='товары', + related_name='order', + through='ProductOrder', + ) + created_at = models.DateTimeField( + 'время создания', + auto_now=True, + ) + + class Meta: + verbose_name = 'заказ' + verbose_name_plural = 'заказы' + + def __str__(self): + return f'{self.firstname} {self.lastname[:1]}., {self.created_at:%d.%m.%y %H:%M:%S}' diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 66ac42173..e7b120ecf 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -1,8 +1,11 @@ +import json + from django.http import JsonResponse from django.templatetags.static import static +from rest_framework.decorators import api_view +from rest_framework.response import Response - -from .models import Product +from .models import Product, Order, ProductOrder def banners_list_api(request): @@ -57,6 +60,17 @@ def product_list_api(request): }) +@api_view(['POST']) def register_order(request): - # TODO это лишь заглушка - return JsonResponse({}) + request_body = request.data + request_products = request_body.pop('products') + order = Order.objects.create(**request_body) + for product_order in request_products: + order.products.add( + product_order['product'], + through_defaults={'quantity': product_order['quantity']} + ) + + return Response({ + 'order_id': order.id + }) diff --git a/package-lock.json b/package-lock.json index 1eba2b329..b94c7285d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3104 +1,20 @@ { - "name": "star-burger", - "lockfileVersion": 2, "requires": true, - "packages": { - "": { - "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/preset-react": "^7.16.7", - "core-js": "^3.21.1", - "lodash": "^4.17.21", - "parcel": "^2.4.0", - "react": "^17.0.2", - "react-bootstrap": "^0.33.1", - "react-dom": "^17.0.2", - "react-transition-group": "^4.4.2" - }, - "devDependencies": { - "@babel/core": "^7.17.8" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dependencies": { - "@babel/highlight": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", - "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.8", - "@babel/parser": "^7.17.8", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.17.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", - "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", - "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", - "dependencies": { - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dependencies": { - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", - "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", - "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", - "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz", - "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz", - "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.16.7", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", - "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz", - "integrity": "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz", - "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-transform-react-display-name": "^7.16.7", - "@babel/plugin-transform-react-jsx": "^7.16.7", - "@babel/plugin-transform-react-jsx-development": "^7.16.7", - "@babel/plugin-transform-react-pure-annotations": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz", - "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs2": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.17.8.tgz", - "integrity": "sha512-KWN7KTjojEVk+hhT7EtvWtSBTueqnPiCT2xPoDFF+ept2Sx9UKnLY7hGsnrNsdx7jvMUQnHoDS6AHCys7i15LA==", - "dependencies": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs2/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.4 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. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true - }, - "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@parcel/bundler-default": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.4.0.tgz", - "integrity": "sha512-RaXlxo0M51739Ko3bsOJpDBZlJ+cqkDoBTozNeSc65jS2TMBIBWLMapm8095qmty39OrgYNhzjgPiIlKDS/LWA==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/hash": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.4.0.tgz", - "integrity": "sha512-oOudoAafrCAHQY0zkU7gVHG1pAGBUz9rht7Tx4WupTmAH0O0F5UnZs6XbjoBJaPHg+CYUXK7v9wQcrNA72E3GA==", - "dependencies": { - "@parcel/fs": "2.4.0", - "@parcel/logger": "2.4.0", - "@parcel/utils": "2.4.0", - "lmdb": "2.2.4" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.4.0" - } - }, - "node_modules/@parcel/codeframe": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.4.0.tgz", - "integrity": "sha512-PJ3W9Z0sjoS2CANyo50c+LEr9IRZrtu0WsVPSYZ5ZYRuSXrSa/6PcAlnkyDk2+hi7Od8ncT2bmDexl0Oar3Jyg==", - "dependencies": { - "chalk": "^4.1.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/compressor-raw": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.4.0.tgz", - "integrity": "sha512-ZErX14fTc0gKIgtnuqW7Clfln4dpXWfUaJQQIf5C3x/LkpUeEhdXeKntkvSxOddDk2JpIKDwqzAxEMZUnDo4Nw==", - "dependencies": { - "@parcel/plugin": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/config-default": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.4.0.tgz", - "integrity": "sha512-pFOPBXPO6HGqNWTLkcK5i8haMOrRgUouUhcWPGWDpN9IPUYFK2E/O1E/uyMjIA1mSL3FnazI+jJwZ45NhKPpIA==", - "dependencies": { - "@parcel/bundler-default": "2.4.0", - "@parcel/compressor-raw": "2.4.0", - "@parcel/namer-default": "2.4.0", - "@parcel/optimizer-css": "2.4.0", - "@parcel/optimizer-htmlnano": "2.4.0", - "@parcel/optimizer-image": "2.4.0", - "@parcel/optimizer-svgo": "2.4.0", - "@parcel/optimizer-terser": "2.4.0", - "@parcel/packager-css": "2.4.0", - "@parcel/packager-html": "2.4.0", - "@parcel/packager-js": "2.4.0", - "@parcel/packager-raw": "2.4.0", - "@parcel/packager-svg": "2.4.0", - "@parcel/reporter-dev-server": "2.4.0", - "@parcel/resolver-default": "2.4.0", - "@parcel/runtime-browser-hmr": "2.4.0", - "@parcel/runtime-js": "2.4.0", - "@parcel/runtime-react-refresh": "2.4.0", - "@parcel/runtime-service-worker": "2.4.0", - "@parcel/transformer-babel": "2.4.0", - "@parcel/transformer-css": "2.4.0", - "@parcel/transformer-html": "2.4.0", - "@parcel/transformer-image": "2.4.0", - "@parcel/transformer-js": "2.4.0", - "@parcel/transformer-json": "2.4.0", - "@parcel/transformer-postcss": "2.4.0", - "@parcel/transformer-posthtml": "2.4.0", - "@parcel/transformer-raw": "2.4.0", - "@parcel/transformer-react-refresh-wrap": "2.4.0", - "@parcel/transformer-svg": "2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.4.0" - } - }, - "node_modules/@parcel/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.4.0.tgz", - "integrity": "sha512-EWZ2UWtIuwDc3fgsKyyTLpNNPoG8Yk2L117ICWF/+cqY8z/wJHm2KwLbeplDeq524shav0GJ9O4CemP3JPx0Nw==", - "dependencies": { - "@parcel/cache": "2.4.0", - "@parcel/diagnostic": "2.4.0", - "@parcel/events": "2.4.0", - "@parcel/fs": "2.4.0", - "@parcel/graph": "2.4.0", - "@parcel/hash": "2.4.0", - "@parcel/logger": "2.4.0", - "@parcel/package-manager": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/types": "2.4.0", - "@parcel/utils": "2.4.0", - "@parcel/workers": "2.4.0", - "abortcontroller-polyfill": "^1.1.9", - "base-x": "^3.0.8", - "browserslist": "^4.6.6", - "clone": "^2.1.1", - "dotenv": "^7.0.0", - "dotenv-expand": "^5.1.0", - "json-source-map": "^0.6.1", - "json5": "^2.2.0", - "msgpackr": "^1.5.4", - "nullthrows": "^1.1.1", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css/-/css-1.7.3.tgz", - "integrity": "sha512-rgdRX4Uk31EvzH/mUScL0wdXtkci3U5N1W2pgam+9S10vQy4uONhWBepZ1tUCjONHLacGXr1jp3LbG/HI7LiTw==", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/css-darwin-arm64": "1.7.3", - "@parcel/css-darwin-x64": "1.7.3", - "@parcel/css-linux-arm-gnueabihf": "1.7.3", - "@parcel/css-linux-arm64-gnu": "1.7.3", - "@parcel/css-linux-arm64-musl": "1.7.3", - "@parcel/css-linux-x64-gnu": "1.7.3", - "@parcel/css-linux-x64-musl": "1.7.3", - "@parcel/css-win32-x64-msvc": "1.7.3" - } - }, - "node_modules/@parcel/css-darwin-arm64": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-darwin-arm64/-/css-darwin-arm64-1.7.3.tgz", - "integrity": "sha512-m3HDY+Rh8HJxmLELKAvCpF59vLS7FWtgBODHxl8G9Jl2CnGtXpXvdpyeMxNsTE+2QuPC+a5QT7IeZAKb2Gjmxg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css-darwin-x64": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-darwin-x64/-/css-darwin-x64-1.7.3.tgz", - "integrity": "sha512-LuhweXKxVwrz/hjAOm9XNRMSL+p23px20nhSCASkyUP7Higaxza948W3TSQdoL3YyR+wQxQH8Yj+R/T8Tz3E3g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css-linux-arm-gnueabihf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm-gnueabihf/-/css-linux-arm-gnueabihf-1.7.3.tgz", - "integrity": "sha512-/pd9Em18zMvt7eDZAMpNBEwF7c4VPVhAtBOZ59ClFrsXCTDNYP7mSy0cwNgtLelCRZCGAQmZNBDNQPH7vO3rew==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css-linux-arm64-gnu": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm64-gnu/-/css-linux-arm64-gnu-1.7.3.tgz", - "integrity": "sha512-5aKiEhQK40riO4iVKzRqISzgYK+7Z7i3e6JTSz+/BHuQyHEUaBe/RuJ8Z0BDQtFz0HmWQlrQCd+7hd0Xgd8vYQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css-linux-arm64-musl": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm64-musl/-/css-linux-arm64-musl-1.7.3.tgz", - "integrity": "sha512-Wf7/aIueDED2JqBMfZvzbBAFSaPmd3TR28bD2pmP7CI/jZnm9vHVKMdOLgt9NKSSSjdGrp+VM410CsrUM7xcOw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css-linux-x64-gnu": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-linux-x64-gnu/-/css-linux-x64-gnu-1.7.3.tgz", - "integrity": "sha512-0ZADbuFklUrHC1p2uPY4BPcN07jUTMqJzr/SSdnGN2XiXgiVZGcDCMHUj0DvC9Vwy11DDM6Rnw4QBbKHG+QGjQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css-linux-x64-musl": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-linux-x64-musl/-/css-linux-x64-musl-1.7.3.tgz", - "integrity": "sha512-mFWWM8lX2OIID81YQuDDt9zTqof0B7UcEcs0huE7Zbs60uLEEQupdf8iH0yh5EOhxPt3sRcQnGXf2QTrXdjIMA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css-win32-x64-msvc": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@parcel/css-win32-x64-msvc/-/css-win32-x64-msvc-1.7.3.tgz", - "integrity": "sha512-KUFEMQcoP7DG3QbsN21OxhjHkfQ1BARn7D9puX75bV5N1F1kv557aaLkQZiMsgiYOL4tmJvsdQXutG7x++3j4Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/diagnostic": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.4.0.tgz", - "integrity": "sha512-TjWO/b2zMFhub5ouwGjazMm7iAUvdmXBfWmjrg4TBhUbhoQwBnyWfvMDtAYo7PcvXfxVPgPZv86Nv6Ym5H6cHQ==", - "dependencies": { - "json-source-map": "^0.6.1", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/events": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.4.0.tgz", - "integrity": "sha512-DEaEtFbhOhNAEmiXJ3MyF8Scq+sNDKiTyLax4lAC5/dpE5GvwfNnoD17C2+0gDuuDpdQkdHfXfvr50aYFt7jcw==", - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/fs": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.4.0.tgz", - "integrity": "sha512-CnUlWGUJ52SJVQi8QnaAPPQZOADmHMV9D9aX9GLcDm5XLT3Em7vmesG4bNLdMLwzYuzAtenhcWmuRCACuYztHw==", - "dependencies": { - "@parcel/fs-search": "2.4.0", - "@parcel/types": "2.4.0", - "@parcel/utils": "2.4.0", - "@parcel/watcher": "^2.0.0", - "@parcel/workers": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.4.0" - } - }, - "node_modules/@parcel/fs-search": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.4.0.tgz", - "integrity": "sha512-W/Vu6wbZk4wuB6AVdMkyymwh/S8Peed/PgJgSsApYD6lSTD315I6OuEdxZh3lWY+dqQdog/NJ7dvi/hdpH/Iqw==", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/graph": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.4.0.tgz", - "integrity": "sha512-5TZIAfDITkJCzgH4j4OQhnIvjV9IFwWqNBJanRl5QQTmKvdcODS3WbnK1SOJ+ZltcLVXMB+HNXmL0bX0tVolcw==", - "dependencies": { - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/hash": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.4.0.tgz", - "integrity": "sha512-nB+wYNUhe6+G8M7vQhdeFXtpYJYwJgBHOPZ7Hd9O2jdlamWjDbw0t/u1dJbYvGJ8ZDtLDwiItawQVpuVdskQ9g==", - "dependencies": { - "detect-libc": "^1.0.3", - "xxhash-wasm": "^0.4.2" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/logger": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.4.0.tgz", - "integrity": "sha512-DqfU0Zcs/0a7VBk+MsjJ80C66w4kM9EbkO3G12NIyEjNeG50ayW2CE9rUuJ91JaM9j0NFM1P82eyLpQPFFaVPw==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/events": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/markdown-ansi": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.4.0.tgz", - "integrity": "sha512-gPUP1xikxHiu2kFyPy35pfuVkFgAmcywO8YDQj7iYcB+k7l4QPpIYFYGXn2QADV4faf66ncMeTD4uYV8c0GqjQ==", - "dependencies": { - "chalk": "^4.1.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/namer-default": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.4.0.tgz", - "integrity": "sha512-DfL+Gx0Tyoa0vsgRpNybXjuKbWNw8MTVpy7Dk7r0btfVsn1jy3SSwlxH4USf76gb00/pK6XBsMp9zn7Z8ePREQ==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/node-resolver-core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.4.0.tgz", - "integrity": "sha512-qiN97XcfW2fYNoYuVEhNKuVPEJKj5ONQl0fqr/NEMmYvWz3bVKjgiXNJwW558elZvCI08gEbdxgyThpuFFQeKQ==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-css": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.4.0.tgz", - "integrity": "sha512-LQmjjOGsHEHKTJqfHR2eJyhWhLXvHP0uOAU+qopBttYYlB2J/vMK9RYAye5cyAb8bQmV8wAdi2mq9rnt7FMSPw==", - "dependencies": { - "@parcel/css": "^1.7.2", - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.4.0", - "browserslist": "^4.6.6", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-htmlnano": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.4.0.tgz", - "integrity": "sha512-02EbeElLgNOAYhGU7fFBahpoKrX5G/yzahpaoKB/ypScM4roSsAMBkGcluboR5L10YRsvfvJEpxvfGyDA3tPmw==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "htmlnano": "^2.0.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "svgo": "^2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-image": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.4.0.tgz", - "integrity": "sha512-Q4onaBMPkDyYxPzrb8ytBUftaQZFepj9dSUgq+ETuHDzkgia0tomDPfCqrw6ld0qvYyANzXTP5+LC4g0i5yh+A==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "@parcel/workers": "2.4.0", - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-svgo": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.4.0.tgz", - "integrity": "sha512-mwvGuCqVuNCAuMlp2maFE/Uz9ud1T1AuX0f6cCRczjFYiwZuIr/0iDdfFzSziOkVo1MRAGAZNa0dRR/UzCZtVg==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "svgo": "^2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-terser": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.4.0.tgz", - "integrity": "sha512-PdCgRgXNSY6R1HTV9VG2MHp1CgUbP5pslCyxvlbUmQAS6bvEpMOpn3qSd+U28o7mGE/qXIhvpDyi808sb+MEcg==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1", - "terser": "^5.2.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/package-manager": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.4.0.tgz", - "integrity": "sha512-21AEfAQnZbHRVViTn7QsPGe/CiGaFaDUH5f0m8qVC7fDjjhC8LM8blkqU72goaO9FbaLMadtEf2txhzly7h/bg==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/fs": "2.4.0", - "@parcel/logger": "2.4.0", - "@parcel/types": "2.4.0", - "@parcel/utils": "2.4.0", - "@parcel/workers": "2.4.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.4.0" - } - }, - "node_modules/@parcel/packager-css": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.4.0.tgz", - "integrity": "sha512-LmPDWzkXi60Oy3WrPF0jPKQxeTwW5hmNBgrcXJMHSu+VcXdaQZNzNxVzhnZkJUbDd2z9vAUrUGzdLh8TquC8iQ==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-html": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.4.0.tgz", - "integrity": "sha512-OPMIQ1uHYQFpRPrsmm5BqONbAyzjlhVsPRAzHlcBrglG4BTUeOR2ow4MUKblHmVVqc3QHnfZG4nHHtFkeuNQ3A==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/types": "2.4.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.4.0.tgz", - "integrity": "sha512-cfslIH43CJFgBS9PmdFaSnbInMCoejsFCnxtJa2GeUpjCXSfelPRp0OPx7m8n+fap4czftPhoxBALeDUElOZGQ==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/hash": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.4.0", - "globals": "^13.2.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-raw": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.4.0.tgz", - "integrity": "sha512-SFfw7chMFITj3J26ZVDJxbO6xwtPFcFBm1js8cwWMgzwuwS6CEc43k5+Abj+2/EqHU9kNJU9eWV5vT6lQwf3HA==", - "dependencies": { - "@parcel/plugin": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-svg": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.4.0.tgz", - "integrity": "sha512-DwkgrdLEQop+tu9Ocr1ZaadmpsbSgVruJPr80xq1LaB0Jiwrl9HjHStMNH1laNFueK1yydxhnj9C2JQfW28qag==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/types": "2.4.0", - "@parcel/utils": "2.4.0", - "posthtml": "^0.16.4" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.4.0.tgz", - "integrity": "sha512-ehFUAL2+h27Lv+cYbbXA74UGy8C+eglUjcpvASOOjVRFuD6poMAMliKkKAXBhQaFx/Rvhz27A2PIPv9lL2i4UQ==", - "dependencies": { - "@parcel/types": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/reporter-cli": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.4.0.tgz", - "integrity": "sha512-Q9bIFMaGvQgypCDxdMEKOwrJzIHAXScKkuFsqTHnUL6mmH3Mo2CoEGAq/wpMXuPhXRn1dPJcHgTNDwZ2fSzz0A==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/types": "2.4.0", - "@parcel/utils": "2.4.0", - "chalk": "^4.1.0", - "term-size": "^2.2.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/reporter-dev-server": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.4.0.tgz", - "integrity": "sha512-24h++wevs7XYuX4dKa4PUfLSstvn3g7udajFv6CeQoME+dR25RL/wH/2LUbhV5ilgXXab76rWIndSqp78xHxPA==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/resolver-default": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.4.0.tgz", - "integrity": "sha512-K7pIIFmGm1hjg/7Mzkg99i8tfCClKfBUTuc2R5j8cdr2n0mCAi4/f2mFf5svLrb5XZrnDgoQ05tHKklLEfUDUw==", - "dependencies": { - "@parcel/node-resolver-core": "2.4.0", - "@parcel/plugin": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-browser-hmr": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.4.0.tgz", - "integrity": "sha512-swPFtvxGoCA9LEjU/pHPNjxG1l0fte8447zXwRN/AaYrtjNu9Ww117OSKCyvCnE143E79jZOFStodTQGFuH+9A==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.4.0.tgz", - "integrity": "sha512-67OOvmkDdtmgzZVP/EyAzoXhJ/Ug3LUVUt7idg9arun5rdJptqEb3Um3wmH0zjcNa9jMbJt7Kl5x1wA8dJgPYg==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-react-refresh": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.4.0.tgz", - "integrity": "sha512-flnr+bf06lMZPbXZZLLaFNrPHvYpfuXTVovEghyUW46qLVpaHj33dpsU/LqZplIuHgBp2ibgrKhr/hY9ell68w==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "react-refresh": "^0.9.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-service-worker": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.4.0.tgz", - "integrity": "sha512-RgM5QUqW22WzstW03CtV+Oih8VGVuwsf94Cc4hLouU2EAD0NUJgATWbFocZVTZIBTKELAWh2gjpSQDdnL4Ur+A==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/source-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.0.2.tgz", - "integrity": "sha512-NnUrPYLpYB6qyx2v6bcRPn/gVigmGG6M6xL8wIg/i0dP1GLkuY1nf+Hqdf63FzPTqqT7K3k6eE5yHPQVMO5jcA==", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": "^12.18.3 || >=14" - } - }, - "node_modules/@parcel/transformer-babel": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.4.0.tgz", - "integrity": "sha512-iWDa7KzJTMP3HNmrYxiYq/S6redk2qminx/9MwmKIN9jzm8mgts2Lj9lOg/t66YaDGky6JAvw4DhB2qW4ni6yQ==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.4.0", - "browserslist": "^4.6.6", - "json5": "^2.2.0", - "nullthrows": "^1.1.1", - "semver": "^5.7.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-css": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.4.0.tgz", - "integrity": "sha512-D2u48LuiQsQvbknABE0wVKFp9r6yCgWrHKEP1J6EJ31c49nXGXDHrpHJJwqq9BvAs/124eBI5mSsehTJyFEMwg==", - "dependencies": { - "@parcel/css": "^1.7.2", - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.4.0", - "browserslist": "^4.6.6", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-html": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.4.0.tgz", - "integrity": "sha512-2/8X/o5QaCNVPr4wkxLCUub7v/YVvVN2L5yCEcTatNeFhNg/2iz7P2ekfqOaoDCHWZEOBT1VTwPbdBt+TMM71Q==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/hash": "2.4.0", - "@parcel/plugin": "2.4.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-html/node_modules/posthtml-parser": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", - "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@parcel/transformer-image": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.4.0.tgz", - "integrity": "sha512-JZkQvGGoGiD0AVKLIbAYYUWxepMmUaWZ4XXx71MmS/kA7cUDwTZ0CXq63YnSY1m+DX+ClTuTN8mBlwe2dkcGbA==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/workers": "2.4.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - } - }, - "node_modules/@parcel/transformer-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.4.0.tgz", - "integrity": "sha512-eeLHFwv3jT3GmIxpLC7B8EXExGK0MFaK91HXljOMh6l8a+GlQYw27MSFQVtoXr0Olx9Uq2uvjXP1+zSsq3LQUQ==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.4.0", - "@parcel/workers": "2.4.0", - "@swc/helpers": "^0.3.6", - "browserslist": "^4.6.6", - "detect-libc": "^1.0.3", - "nullthrows": "^1.1.1", - "regenerator-runtime": "^0.13.7", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-json": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.4.0.tgz", - "integrity": "sha512-3nR+d39mbURoXIypDfVCaxpwL65qMV+h8SLD78up2uhaRGklHQfN7GuemR7L+mcVAgNrmwVvZHhyNjdgYwWqqg==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "json5": "^2.2.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-postcss": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.4.0.tgz", - "integrity": "sha512-ijIa2x+dbKnJhr7zO5WlXkvuj832fDoGksMBk2DX3u2WMrbh2rqVWPpGFsDhESx7EAy38nUoV/5KUdrNqUmCEA==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/hash": "2.4.0", - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "clone": "^2.1.1", - "nullthrows": "^1.1.1", - "postcss-value-parser": "^4.2.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-posthtml": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.4.0.tgz", - "integrity": "sha512-xoL3AzgtVeRRAo6bh0AHAYm9bt1jZ+HiH86/7oARj/uJs6Wd8kXK/DZf6fH+F87hj4e7bnjmDDc0GPVK0lPz1w==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-posthtml/node_modules/posthtml-parser": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", - "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@parcel/transformer-raw": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.4.0.tgz", - "integrity": "sha512-fciFbNrzj0kLlDgr6OsI0PUv414rVygDWAsgbCCq4BexDkuemMs9f9FjMctx9B2VZlctE8dTT4RGkuQumTIpUg==", - "dependencies": { - "@parcel/plugin": "2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-react-refresh-wrap": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.4.0.tgz", - "integrity": "sha512-9+f6sGOWkf0jyUQ1CuFWk+04Mq3KTOCU9kRiwCHX1YdUCv5uki6r9XUSpqiYodrV+L6w9CCwLvGMLCDHxtCxMg==", - "dependencies": { - "@parcel/plugin": "2.4.0", - "@parcel/utils": "2.4.0", - "react-refresh": "^0.9.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-svg": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.4.0.tgz", - "integrity": "sha512-D+yzVtSxtQML3d26fd/g4E/xYW68+OMbMUVLXORtoYMU42fnXQkJP6jGOdqy8Td+WORNY7EwVtQnESLwhBmolw==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/hash": "2.4.0", - "@parcel/plugin": "2.4.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-svg/node_modules/posthtml-parser": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", - "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@parcel/types": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.4.0.tgz", - "integrity": "sha512-nysGIbBEnp+7R+tKTysdcTFOZDTCodsiXFeAhYQa5bhiOnG1l9gzhxQnE2OsdsgvMm40IOsgKprqvM/DbdLfnQ==", - "dependencies": { - "@parcel/cache": "2.4.0", - "@parcel/diagnostic": "2.4.0", - "@parcel/fs": "2.4.0", - "@parcel/package-manager": "2.4.0", - "@parcel/source-map": "^2.0.0", - "@parcel/workers": "2.4.0", - "utility-types": "^3.10.0" - } - }, - "node_modules/@parcel/utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.4.0.tgz", - "integrity": "sha512-sdNo+mZqDZT8LJYB6WWRKa4wFVZcK6Zb5Jh6Du76QvXXwHbPIQNZgJBb6gd/Rbk4GLOp2tW7MnBfq6zP9E9E2g==", - "dependencies": { - "@parcel/codeframe": "2.4.0", - "@parcel/diagnostic": "2.4.0", - "@parcel/hash": "2.4.0", - "@parcel/logger": "2.4.0", - "@parcel/markdown-ansi": "2.4.0", - "@parcel/source-map": "^2.0.0", - "chalk": "^4.1.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.5.tgz", - "integrity": "sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw==", - "hasInstallScript": true, - "dependencies": { - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/workers": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.4.0.tgz", - "integrity": "sha512-eSFyvEoXXPgFzQfKIlpkUjpHfIbezUCRFTPKyJAKCxvU5DSXOpb1kz5vDESWQ4qTZXKnrKvxS1PPWN6bam9z0g==", - "dependencies": { - "@parcel/diagnostic": "2.4.0", - "@parcel/logger": "2.4.0", - "@parcel/types": "2.4.0", - "@parcel/utils": "2.4.0", - "chrome-trace-event": "^1.0.2", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.4.0" - } - }, - "node_modules/@swc/helpers": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.8.tgz", - "integrity": "sha512-aWItSZvJj4+GI6FWkjZR13xPNPctq2RRakzo+O6vN7bC2yjwdg5EFpgaSAUn95b7BGSgcflvzVDPoKmJv24IOg==" - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.4", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" - }, - "node_modules/@types/react": { - "version": "17.0.43", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", - "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "node_modules/abortcontroller-polyfill": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", - "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==" - }, - "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "node_modules/browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001320", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", - "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/core-js": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", - "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/css-select": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", - "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dependencies": { - "css-tree": "^1.1.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csstype": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", - "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.92", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.92.tgz", - "integrity": "sha512-YAVbvQIcDE/IJ/vzDMjD484/hsRbFPW2qXJPaYTfOhtligmfYEYOep+5QojpaEU9kq6bMvNeC2aG7arYvTHYsA==" - }, - "node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-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=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-port": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", - "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/htmlnano": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.0.tgz", - "integrity": "sha512-thKQfhcp2xgtsWNE27A2bliEeqVL5xjAgGn0wajyttvFFsvFWWah1ntV9aEX61gz0T6MBQ5xK/1lXuEumhJTcg==", - "dependencies": { - "cosmiconfig": "^7.0.1", - "posthtml": "^0.16.5", - "timsort": "^0.3.0" - }, - "peerDependencies": { - "cssnano": "^5.0.11", - "postcss": "^8.3.11", - "purgecss": "^4.0.3", - "relateurl": "^0.2.7", - "srcset": "^5.0.0", - "svgo": "^2.8.0", - "terser": "^5.10.0", - "uncss": "^0.17.3" - }, - "peerDependenciesMeta": { - "cssnano": { - "optional": true - }, - "postcss": { - "optional": true - }, - "purgecss": { - "optional": true - }, - "relateurl": { - "optional": true - }, - "srcset": { - "optional": true - }, - "svgo": { - "optional": true - }, - "terser": { - "optional": true - }, - "uncss": { - "optional": true - } - } - }, - "node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "node_modules/is-json": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", - "integrity": "sha1-a+Fm0USCihMdaGiRuYPfYsOUkf8=" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/json-source-map/-/json-source-map-0.6.1.tgz", - "integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==" - }, - "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keycode": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", - "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/lmdb": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.2.4.tgz", - "integrity": "sha512-gto+BB2uEob8qRiTlOq+R3uX0YNHsX9mjxj9Sbdue/LIKqu6IlZjrsjKeGyOMquc/474GEqFyX2pdytpydp0rQ==", - "hasInstallScript": true, - "dependencies": { - "msgpackr": "^1.5.4", - "nan": "^2.14.2", - "node-gyp-build": "^4.2.3", - "ordered-binary": "^1.2.4", - "weak-lru-cache": "^1.2.2" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/msgpackr": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.5.5.tgz", - "integrity": "sha512-JG0V47xRIQ9pyUnx6Hb4+3TrQoia2nA3UIdmyTldhxaxtKFkekkKpUW/N6fwHwod9o4BGuJGtouxOk+yCP5PEA==", - "optionalDependencies": { - "msgpackr-extract": "^1.0.14" - } - }, - "node_modules/msgpackr-extract": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-1.0.16.tgz", - "integrity": "sha512-fxdRfQUxPrL/TizyfYfMn09dK58e+d65bRD/fcaVH4052vj30QOzzqxcQIS7B0NsqlypEQ/6Du3QmP2DhWFfCA==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "nan": "^2.14.2", - "node-gyp-build": "^4.2.3" - } - }, - "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" - }, - "node_modules/node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" - }, - "node_modules/nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" - }, - "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=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ordered-binary": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.2.4.tgz", - "integrity": "sha512-A/csN0d3n+igxBPfUrjbV5GC69LWj2pjZzAAeeHXLukQ4+fytfP4T1Lg0ju7MSPSwq7KtHkGaiwO8URZN5IpLg==" - }, - "node_modules/parcel": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.4.0.tgz", - "integrity": "sha512-dPWpu4RnxG9HqiLvaF8COEWEnT/KrigrC6PyPaQ0zEgpBfp7/jzXZFBVaZk2N+lpvrbNEYMjN9bv5UQGJJszIw==", - "dependencies": { - "@parcel/config-default": "2.4.0", - "@parcel/core": "2.4.0", - "@parcel/diagnostic": "2.4.0", - "@parcel/events": "2.4.0", - "@parcel/fs": "2.4.0", - "@parcel/logger": "2.4.0", - "@parcel/package-manager": "2.4.0", - "@parcel/reporter-cli": "2.4.0", - "@parcel/reporter-dev-server": "2.4.0", - "@parcel/utils": "2.4.0", - "chalk": "^4.1.0", - "commander": "^7.0.0", - "get-port": "^4.2.0", - "v8-compile-cache": "^2.0.0" - }, - "bin": { - "parcel": "lib/bin.js" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/posthtml": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", - "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", - "dependencies": { - "posthtml-parser": "^0.11.0", - "posthtml-render": "^3.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/posthtml-parser": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", - "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/posthtml-render": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", - "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", - "dependencies": { - "is-json": "^2.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types-extra": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", - "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", - "dependencies": { - "react-is": "^16.3.2", - "warning": "^4.0.0" - }, - "peerDependencies": { - "react": ">=0.14.0" - } - }, - "node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-bootstrap": { - "version": "0.33.1", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.33.1.tgz", - "integrity": "sha512-qWTRravSds87P8WC82tETy2yIso8qDqlIm0czsrduCaYAFtHuyLu0XDbUlfLXeRzqgwm5sRk2wRaTNoiVkk/YQ==", - "dependencies": { - "@babel/runtime-corejs2": "^7.0.0", - "classnames": "^2.2.5", - "dom-helpers": "^3.2.0", - "invariant": "^2.2.4", - "keycode": "^2.2.0", - "prop-types": "^15.6.1", - "prop-types-extra": "^1.0.1", - "react-overlays": "^0.9.0", - "react-prop-types": "^0.4.0", - "react-transition-group": "^2.0.0", - "uncontrollable": "^7.0.2", - "warning": "^3.0.0" - }, - "peerDependencies": { - "react": ">=16.3.0", - "react-dom": ">=16.3.0" - } - }, - "node_modules/react-bootstrap/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "dependencies": { - "@babel/runtime": "^7.1.2" - } - }, - "node_modules/react-bootstrap/node_modules/react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", - "dependencies": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" - }, - "peerDependencies": { - "react": ">=15.0.0", - "react-dom": ">=15.0.0" - } - }, - "node_modules/react-bootstrap/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-overlays": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.9.3.tgz", - "integrity": "sha512-u2T7nOLnK+Hrntho4p0Nxh+BsJl0bl4Xuwj/Y0a56xywLMetgAfyjnDVrudLXsNcKGaspoC+t3C1V80W9QQTdQ==", - "dependencies": { - "classnames": "^2.2.5", - "dom-helpers": "^3.2.1", - "prop-types": "^15.5.10", - "prop-types-extra": "^1.0.1", - "react-transition-group": "^2.2.1", - "warning": "^3.0.0" - }, - "peerDependencies": { - "react": ">=16.3.0", - "react-dom": ">=16.3.0" - } - }, - "node_modules/react-overlays/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "dependencies": { - "@babel/runtime": "^7.1.2" - } - }, - "node_modules/react-overlays/node_modules/react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", - "dependencies": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" - }, - "peerDependencies": { - "react": ">=15.0.0", - "react-dom": ">=15.0.0" - } - }, - "node_modules/react-overlays/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/react-prop-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", - "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=", - "dependencies": { - "warning": "^3.0.0" - }, - "peerDependencies": { - "react": ">=0.14.0" - } - }, - "node_modules/react-prop-types/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/react-refresh": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", - "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", - "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", - "dependencies": { - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, - "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=", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/uncontrollable": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", - "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", - "dependencies": { - "@babel/runtime": "^7.6.3", - "@types/react": ">=16.9.11", - "invariant": "^2.2.4", - "react-lifecycles-compat": "^3.0.4" - }, - "peerDependencies": { - "react": ">=15.0.0" - } - }, - "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" - }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" + "lockfileVersion": 1, + "dependencies": { + "16": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/16/-/16-0.0.2.tgz", + "integrity": "sha512-AhG4lpdn+/it+U5Xl1bm5SbaHYTH5NfU/vXZkP7E7CHjtVtITuFVZKa3AZP3gN38RDJHYYtEqWmqzCutlXaR7w==", + "requires": { + "numeric": "^1.2.6" } }, - "node_modules/weak-lru-cache": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", - "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==" - }, - "node_modules/xxhash-wasm": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", - "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - } - }, - "dependencies": { "@ampproject/remapping": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.0" } @@ -3114,12 +30,14 @@ "@babel/compat-data": { "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==" + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true }, "@babel/core": { "version": "7.17.8", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", + "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -3141,7 +59,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -3174,6 +93,7 @@ "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, "requires": { "@babel/compat-data": "^7.17.7", "@babel/helper-validator-option": "^7.16.7", @@ -3184,7 +104,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -3256,6 +177,7 @@ "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -3296,6 +218,7 @@ "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, "requires": { "@babel/types": "^7.17.0" } @@ -3322,6 +245,7 @@ "version": "7.17.8", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dev": true, "requires": { "@babel/template": "^7.16.7", "@babel/traverse": "^7.17.3", @@ -3526,17 +450,20 @@ "@jridgewell/resolve-uri": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true }, "@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4381,6 +1308,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, "requires": { "safe-buffer": "~5.1.1" }, @@ -4388,7 +1316,8 @@ "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==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true } } }, @@ -4551,7 +1480,8 @@ "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==" + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true }, "get-port": { "version": "4.2.0", @@ -4740,6 +1670,11 @@ "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" }, + "numeric": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz", + "integrity": "sha512-avBiDAP8siMa7AfJgYyuxw1oyII4z2sswS23+O+ZfV28KrtNzy0wxUFwi4f3RyM4eeeXNs1CThxR7pb5QQcMiw==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index c20ddfa87..219886538 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "16": "0.0.2", "@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/preset-react": "^7.16.7", "core-js": "^3.21.1", diff --git a/requirements.txt b/requirements.txt index 318fee174..de403e5aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,5 @@ django-debug-toolbar==3.2.1 Pillow==9.4.0 environs[django]==9.3.2 wheel==0.38.4 +django-phonenumber-field[phonenumbers]==7.0.2 +djangorestframework==3.14.0 diff --git a/star_burger/settings.py b/star_burger/settings.py index a23d4133c..9d11fc1d1 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -27,6 +27,8 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'debug_toolbar', + 'phonenumber_field', + 'rest_framework', ] MIDDLEWARE = [ @@ -122,3 +124,6 @@ os.path.join(BASE_DIR, "assets"), os.path.join(BASE_DIR, "bundles"), ] + +PHONENUMBER_DEFAULT_REGION = 'RU' + diff --git a/star_burger/urls.py b/star_burger/urls.py index e194cafe4..e157b9964 100644 --- a/star_burger/urls.py +++ b/star_burger/urls.py @@ -26,6 +26,7 @@ path('', render, kwargs={'template_name': 'index.html'}, name='start_page'), path('api/', include('foodcartapp.urls')), path('manager/', include('restaurateur.urls')), + path('api-auth/', include('rest_framework.urls')), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: From 767d2ceb3fab246d7267c792493f3cb0ddda6b91 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 9 Mar 2023 22:47:14 +0300 Subject: [PATCH 03/67] add product validation for register_order --- foodcartapp/views.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/foodcartapp/views.py b/foodcartapp/views.py index e7b120ecf..45910bdaf 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -2,6 +2,7 @@ from django.http import JsonResponse from django.templatetags.static import static +from rest_framework import status from rest_framework.decorators import api_view from rest_framework.response import Response @@ -62,9 +63,15 @@ def product_list_api(request): @api_view(['POST']) def register_order(request): - request_body = request.data - request_products = request_body.pop('products') - order = Order.objects.create(**request_body) + + if not isinstance(request.data.get('products'), list) or not request.data.get('products'): + return Response( + {'error': 'products not provided or not a list'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + request_products = request.data.pop('products') + order = Order.objects.create(**request.data) for product_order in request_products: order.products.add( product_order['product'], From bc97f786fe4619bb01437afbf4b8de933e444159 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 9 Mar 2023 23:16:46 +0300 Subject: [PATCH 04/67] extend data validation for register_order --- foodcartapp/views.py | 46 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 45910bdaf..92918b242 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -1,12 +1,11 @@ -import json - from django.http import JsonResponse from django.templatetags.static import static +from phonenumber_field.phonenumber import PhoneNumber from rest_framework import status from rest_framework.decorators import api_view from rest_framework.response import Response -from .models import Product, Order, ProductOrder +from .models import Product, Order def banners_list_api(request): @@ -63,13 +62,50 @@ def product_list_api(request): @api_view(['POST']) def register_order(request): + required_fields = ['products', 'firstname', 'lastname', 'phonenumber', 'address'] + if not all([request.data.get(field) for field in required_fields]): + return Response( + {'error': f'missing one or more required fields: {", ".join(required_fields)}'}, + status=status.HTTP_400_BAD_REQUEST, + ) - if not isinstance(request.data.get('products'), list) or not request.data.get('products'): + required_product_fields = ['product', 'quantity'] + for product in request.data['products']: + if not all([product.get(field) for field in required_product_fields]): + return Response( + {'error': f'each product must have these int fields: {", ".join(required_product_fields)}'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if not isinstance(request.data.get('products'), list): return Response( - {'error': 'products not provided or not a list'}, + {'error': 'products must be a list'}, status=status.HTTP_400_BAD_REQUEST, ) + str_fields = ['firstname', 'lastname', 'phonenumber', 'address'] + if not all([isinstance(request.data[field], str) for field in str_fields]): + return Response( + {'error': 'wrong data type for one or more string fields'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if not PhoneNumber.from_string(request.data['phonenumber']).is_valid(): + return Response( + {'error': 'invalid phone number'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + for product in request.data['products']: + try: + Product.objects.get(pk=product.get('product')) + except Product.DoesNotExist: + return Response( + {'error': 'product with the specified id does not exist'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + request_products = request.data.pop('products') order = Order.objects.create(**request.data) for product_order in request_products: From 11d9530e033cdd5ad6a70aaf8cb788760496fd58 Mon Sep 17 00:00:00 2001 From: mavel Date: Fri, 10 Mar 2023 18:22:06 +0300 Subject: [PATCH 05/67] use DRF ModelSerializer for validation --- foodcartapp/views.py | 70 ++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 51 deletions(-) diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 92918b242..bb2282f3b 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -1,11 +1,10 @@ from django.http import JsonResponse from django.templatetags.static import static -from phonenumber_field.phonenumber import PhoneNumber -from rest_framework import status from rest_framework.decorators import api_view from rest_framework.response import Response +from rest_framework.serializers import ModelSerializer -from .models import Product, Order +from .models import Product, Order, ProductOrder def banners_list_api(request): @@ -60,60 +59,29 @@ def product_list_api(request): }) -@api_view(['POST']) -def register_order(request): - required_fields = ['products', 'firstname', 'lastname', 'phonenumber', 'address'] - if not all([request.data.get(field) for field in required_fields]): - return Response( - {'error': f'missing one or more required fields: {", ".join(required_fields)}'}, - status=status.HTTP_400_BAD_REQUEST, - ) - - required_product_fields = ['product', 'quantity'] - for product in request.data['products']: - if not all([product.get(field) for field in required_product_fields]): - return Response( - {'error': f'each product must have these int fields: {", ".join(required_product_fields)}'}, - status=status.HTTP_400_BAD_REQUEST, - ) - - if not isinstance(request.data.get('products'), list): - return Response( - {'error': 'products must be a list'}, - status=status.HTTP_400_BAD_REQUEST, - ) +class ProductOrderSerializer(ModelSerializer): + class Meta: + model = ProductOrder + fields = ['product', 'quantity'] - str_fields = ['firstname', 'lastname', 'phonenumber', 'address'] - if not all([isinstance(request.data[field], str) for field in str_fields]): - return Response( - {'error': 'wrong data type for one or more string fields'}, - status=status.HTTP_400_BAD_REQUEST, - ) - if not PhoneNumber.from_string(request.data['phonenumber']).is_valid(): - return Response( - {'error': 'invalid phone number'}, - status=status.HTTP_400_BAD_REQUEST, - ) +class OrderSerializer(ModelSerializer): + products = ProductOrderSerializer(many=True, allow_empty=False) - for product in request.data['products']: - try: - Product.objects.get(pk=product.get('product')) - except Product.DoesNotExist: - return Response( - {'error': 'product with the specified id does not exist'}, - status=status.HTTP_400_BAD_REQUEST, - ) + class Meta: + model = Order + fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products'] - request_products = request.data.pop('products') - order = Order.objects.create(**request.data) - for product_order in request_products: +@api_view(['POST']) +def register_order(request): + serializer = OrderSerializer(data=request.data, write_only=True) + serializer.is_valid(raise_exception=True) + products_in_order = serializer.validated_data.pop('products') + order = Order.objects.create(**serializer.validated_data) + for product_order in products_in_order: order.products.add( product_order['product'], through_defaults={'quantity': product_order['quantity']} ) - - return Response({ - 'order_id': order.id - }) + return Response({'order_id': order.id}) From 0cd04a4eeeacc7734b7b73ac483bc131c104bb7b Mon Sep 17 00:00:00 2001 From: mavel Date: Fri, 10 Mar 2023 18:30:34 +0300 Subject: [PATCH 06/67] return order data from view --- foodcartapp/views.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/foodcartapp/views.py b/foodcartapp/views.py index bb2282f3b..8c03305c5 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -75,13 +75,18 @@ class Meta: @api_view(['POST']) def register_order(request): - serializer = OrderSerializer(data=request.data, write_only=True) + serializer = OrderSerializer(data=request.data) serializer.is_valid(raise_exception=True) - products_in_order = serializer.validated_data.pop('products') - order = Order.objects.create(**serializer.validated_data) + products_in_order = serializer.validated_data['products'] + order = Order.objects.create( + firstname=serializer.validated_data['firstname'], + lastname=serializer.validated_data['lastname'], + phonenumber=serializer.validated_data['phonenumber'], + address=serializer.validated_data['address'], + ) for product_order in products_in_order: order.products.add( product_order['product'], through_defaults={'quantity': product_order['quantity']} ) - return Response({'order_id': order.id}) + return Response(serializer.data) From a4bd5dd3d669a5b741fe4e8703c0eb4aac5c11fc Mon Sep 17 00:00:00 2001 From: mavel Date: Fri, 10 Mar 2023 21:20:51 +0300 Subject: [PATCH 07/67] list orders in manager UI --- restaurateur/templates/order_items.html | 10 +++++----- restaurateur/views.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index 3ba791723..839da7e8e 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -19,12 +19,12 @@

Необработанные заказы

Адрес доставки - {% for item in order_items %} + {% for order in orders %} - - - - - - - - + {{ order.id }} + {{ order.firstname }} {{ order.lastname }} + {{ order.phonenumber }} + {{ order.address }} {% endfor %} diff --git a/restaurateur/views.py b/restaurateur/views.py index e66d4e32b..49d72bc87 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -7,8 +7,7 @@ from django.contrib.auth import authenticate, login from django.contrib.auth import views as auth_views - -from foodcartapp.models import Product, Restaurant +from foodcartapp.models import Product, Restaurant, Order class Login(forms.Form): @@ -92,6 +91,7 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): + return render(request, template_name='order_items.html', context={ - # TODO заглушка для нереализованного функционала + 'orders': Order.objects.all() }) From b7dc9d539420f7e40ef8a6376530135323f328b3 Mon Sep 17 00:00:00 2001 From: mavel Date: Fri, 10 Mar 2023 22:32:35 +0300 Subject: [PATCH 08/67] show order totals in manager UI --- foodcartapp/models.py | 22 +++++++++++++++++++--- restaurateur/templates/order_items.html | 2 ++ restaurateur/views.py | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/foodcartapp/models.py b/foodcartapp/models.py index 77f367bce..64baf986f 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -1,5 +1,6 @@ -from django.db import models from django.core.validators import MinValueValidator +from django.db import models +from django.db.models import F from phonenumber_field.modelfields import PhoneNumberField @@ -124,9 +125,22 @@ def __str__(self): return f"{self.restaurant.name} - {self.product.name}" +class OrderQuerySet(models.QuerySet): + def with_totals(self): + return self.annotate(total=F('products_ordered__product__price') * F('products_ordered__quantity')) + + class ProductOrder(models.Model): - product = models.ForeignKey('Product', on_delete=models.CASCADE) - order = models.ForeignKey('Order', on_delete=models.CASCADE) + product = models.ForeignKey( + 'Product', + on_delete=models.CASCADE, + related_name='orders', + ) + order = models.ForeignKey( + 'Order', + on_delete=models.CASCADE, + related_name='products_ordered' + ) quantity = models.IntegerField('количество') def __str__(self): @@ -162,6 +176,8 @@ class Order(models.Model): auto_now=True, ) + objects = OrderQuerySet.as_manager() + class Meta: verbose_name = 'заказ' verbose_name_plural = 'заказы' diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index 839da7e8e..e1f86f11c 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -14,6 +14,7 @@

Необработанные заказы

+ @@ -22,6 +23,7 @@

Необработанные заказы

{% for order in orders %} + diff --git a/restaurateur/views.py b/restaurateur/views.py index 49d72bc87..02b01982c 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -93,5 +93,5 @@ def view_restaurants(request): def view_orders(request): return render(request, template_name='order_items.html', context={ - 'orders': Order.objects.all() + 'orders': Order.objects.with_totals() }) From 3282988e8e26d3126c58b0a9e162d0a42768a9a9 Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 11 Mar 2023 01:10:09 +0300 Subject: [PATCH 09/67] save item price for each order --- foodcartapp/admin.py | 18 ++++++++++ .../migrations/0039_auto_20230310_2031.py | 36 +++++++++++++++++++ .../migrations/0040_auto_20230310_2031.py | 28 +++++++++++++++ foodcartapp/models.py | 11 ++++-- foodcartapp/views.py | 8 +++-- 5 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 foodcartapp/migrations/0039_auto_20230310_2031.py create mode 100644 foodcartapp/migrations/0040_auto_20230310_2031.py diff --git a/foodcartapp/admin.py b/foodcartapp/admin.py index ca5b3b955..6da13e5cd 100644 --- a/foodcartapp/admin.py +++ b/foodcartapp/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin +from django.forms import ModelForm from django.shortcuts import reverse from django.templatetags.static import static from django.utils.html import format_html @@ -107,12 +108,29 @@ def get_image_list_preview(self, obj): class ProductAdmin(admin.ModelAdmin): pass +class ProductOrderInlineForm(ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['product_price'].label = 'Цена (оставьте поле пустым для стандартной цены)' + class ProductOrderInline(admin.TabularInline): model = ProductOrder + fields = ('product', 'product_price', 'quantity') + form = ProductOrderInlineForm @admin.register(Order) class OrderAdmin(admin.ModelAdmin): inlines = [ProductOrderInline] list_display = ('firstname', 'lastname', 'phonenumber', 'created_at') + + def save_formset(self, request, form, formset, change): + instances = formset.save(commit=False) + for obj in formset.deleted_objects: + obj.delete() + for instance in instances: + if instance.product_price is None: + instance.product_price = instance.product.price + instance.save() + formset.save_m2m() diff --git a/foodcartapp/migrations/0039_auto_20230310_2031.py b/foodcartapp/migrations/0039_auto_20230310_2031.py new file mode 100644 index 000000000..559f00aa6 --- /dev/null +++ b/foodcartapp/migrations/0039_auto_20230310_2031.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.15 on 2023-03-10 20:31 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0038_auto_20230309_1702'), + ] + + operations = [ + migrations.AddField( + model_name='productorder', + name='product_price', + field=models.DecimalField(decimal_places=2, default=0, max_digits=8, validators=[django.core.validators.MinValueValidator(0)], verbose_name='цена товара'), + preserve_default=False, + ), + migrations.AlterField( + model_name='productorder', + name='order', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products_ordered', to='foodcartapp.order'), + ), + migrations.AlterField( + model_name='productorder', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='foodcartapp.product'), + ), + migrations.AlterField( + model_name='productorder', + name='quantity', + field=models.PositiveIntegerField(verbose_name='количество'), + ), + ] diff --git a/foodcartapp/migrations/0040_auto_20230310_2031.py b/foodcartapp/migrations/0040_auto_20230310_2031.py new file mode 100644 index 000000000..ce06526a5 --- /dev/null +++ b/foodcartapp/migrations/0040_auto_20230310_2031.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.15 on 2023-03-10 20:16 + +from django.db import migrations + + +def add_prices(apps, schema_editor): + ProductOrder = apps.get_model('foodcartapp', 'ProductOrder') + for product_order in ProductOrder.objects.prefetch_related('product').iterator(): + product_order.product_price = product_order.product.price + product_order.save() + + +def reverse(apps, schema_editor): + ProductOrder = apps.get_model('foodcartapp', 'ProductOrder') + for product_order in ProductOrder.objects.iterator(): + product_order.product_price = 0 + product_order.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0039_auto_20230310_2031'), + ] + + operations = [ + migrations.RunPython(add_prices, reverse), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index 64baf986f..a2b71ff61 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -127,7 +127,7 @@ def __str__(self): class OrderQuerySet(models.QuerySet): def with_totals(self): - return self.annotate(total=F('products_ordered__product__price') * F('products_ordered__quantity')) + return self.annotate(total=F('products_ordered__product_price') * F('products_ordered__quantity')) class ProductOrder(models.Model): @@ -141,7 +141,14 @@ class ProductOrder(models.Model): on_delete=models.CASCADE, related_name='products_ordered' ) - quantity = models.IntegerField('количество') + quantity = models.PositiveIntegerField('количество') + product_price = models.DecimalField( + 'цена товара', + max_digits=8, + decimal_places=2, + validators=[MinValueValidator(0)], + blank=True, + ) def __str__(self): return f'{self.product}, {self.quantity}' diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 8c03305c5..1bde38ea0 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -85,8 +85,12 @@ def register_order(request): address=serializer.validated_data['address'], ) for product_order in products_in_order: + product = product_order['product'] order.products.add( - product_order['product'], - through_defaults={'quantity': product_order['quantity']} + product, + through_defaults={ + 'quantity': product_order['quantity'], + 'product_price': product.price, + } ) return Response(serializer.data) From c6f5db8856509c7546a84e071234a6ebf0ba3f07 Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 11 Mar 2023 16:34:13 +0300 Subject: [PATCH 10/67] make register_order atomic --- foodcartapp/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 1bde38ea0..757cfe53e 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -1,3 +1,4 @@ +from django.db import transaction from django.http import JsonResponse from django.templatetags.static import static from rest_framework.decorators import api_view @@ -73,6 +74,7 @@ class Meta: fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products'] +@transaction.atomic @api_view(['POST']) def register_order(request): serializer = OrderSerializer(data=request.data) From b7af883e3eb3250ef0906f038d52f168fac1f784 Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 11 Mar 2023 18:07:47 +0300 Subject: [PATCH 11/67] add link to edit orders on admin site --- restaurateur/templates/order_items.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index e1f86f11c..9897c8af6 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -18,6 +18,7 @@

Необработанные заказы

+ {% for order in orders %} @@ -27,6 +28,7 @@

Необработанные заказы

+ {% endfor %}
ID заказаСтоимость Клиент Телефон Адрес доставки
{{ order.id }}{{ order.total|floatformat:"2g" }} ₽ {{ order.firstname }} {{ order.lastname }} {{ order.phonenumber }} {{ order.address }}Клиент Телефон Адрес доставкиСсылка на админку
{{ order.firstname }} {{ order.lastname }} {{ order.phonenumber }} {{ order.address }}Редактировать
From 1cb5bd228786e9abaa3ee4a642ab50a49fc9b27a Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 11 Mar 2023 22:52:08 +0300 Subject: [PATCH 12/67] redirect back to orders page after editing order --- foodcartapp/admin.py | 11 +++++++++++ foodcartapp/views.py | 1 - restaurateur/templates/order_items.html | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/foodcartapp/admin.py b/foodcartapp/admin.py index 6da13e5cd..6d9378d51 100644 --- a/foodcartapp/admin.py +++ b/foodcartapp/admin.py @@ -1,8 +1,10 @@ from django.contrib import admin from django.forms import ModelForm +from django.http import HttpResponseRedirect from django.shortcuts import reverse from django.templatetags.static import static from django.utils.html import format_html +from django.utils.http import url_has_allowed_host_and_scheme from .models import Product from .models import ProductCategory @@ -10,6 +12,7 @@ from .models import RestaurantMenuItem from .models import Order from .models import ProductOrder +from star_burger.settings import ALLOWED_HOSTS class RestaurantMenuItemInline(admin.TabularInline): @@ -134,3 +137,11 @@ def save_formset(self, request, form, formset, change): instance.product_price = instance.product.price instance.save() formset.save_m2m() + + def response_change(self, request, obj): + response = super().response_change(request, obj) + if "next" in request.GET: + if url_has_allowed_host_and_scheme(request.GET['next'], ALLOWED_HOSTS): + return HttpResponseRedirect(request.GET['next']) + else: + return response diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 757cfe53e..4e2c9981e 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -7,7 +7,6 @@ from .models import Product, Order, ProductOrder - def banners_list_api(request): # FIXME move data to db? return JsonResponse([ diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index 9897c8af6..4b4a8c3e1 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -28,7 +28,7 @@

Необработанные заказы

{{ order.firstname }} {{ order.lastname }} {{ order.phonenumber }} {{ order.address }} - Редактировать + Редактировать {% endfor %} From 4a2c4d544ad3005531a8b44ae07300a6f023cbed Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 11 Mar 2023 23:25:32 +0300 Subject: [PATCH 13/67] add order status to order model and manager UI --- .../migrations/0041_auto_20230311_2021.py | 24 +++++++++++++++++++ .../migrations/0042_alter_order_status.py | 18 ++++++++++++++ foodcartapp/models.py | 23 ++++++++++++++---- restaurateur/templates/order_items.html | 2 ++ restaurateur/views.py | 2 +- 5 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 foodcartapp/migrations/0041_auto_20230311_2021.py create mode 100644 foodcartapp/migrations/0042_alter_order_status.py diff --git a/foodcartapp/migrations/0041_auto_20230311_2021.py b/foodcartapp/migrations/0041_auto_20230311_2021.py new file mode 100644 index 000000000..556585188 --- /dev/null +++ b/foodcartapp/migrations/0041_auto_20230311_2021.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.15 on 2023-03-11 20:21 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0040_auto_20230310_2031'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='status', + field=models.SmallIntegerField(choices=[(0, 'Не обработан'), (1, 'Готовится'), (2, 'В пути'), (3, 'Доставлен')], default=0, verbose_name='статус'), + ), + migrations.AlterField( + model_name='productorder', + name='product_price', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, validators=[django.core.validators.MinValueValidator(0)], verbose_name='цена товара'), + ), + ] diff --git a/foodcartapp/migrations/0042_alter_order_status.py b/foodcartapp/migrations/0042_alter_order_status.py new file mode 100644 index 000000000..6a08c1206 --- /dev/null +++ b/foodcartapp/migrations/0042_alter_order_status.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.15 on 2023-03-11 20:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0041_auto_20230311_2021'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='status', + field=models.SmallIntegerField(choices=[(0, 'Не обработан'), (1, 'Готовится'), (2, 'В пути'), (3, 'Доставлен')], db_index=True, default=0, verbose_name='статус'), + ), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index a2b71ff61..3008317bf 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -125,11 +125,6 @@ def __str__(self): return f"{self.restaurant.name} - {self.product.name}" -class OrderQuerySet(models.QuerySet): - def with_totals(self): - return self.annotate(total=F('products_ordered__product_price') * F('products_ordered__quantity')) - - class ProductOrder(models.Model): product = models.ForeignKey( 'Product', @@ -154,7 +149,19 @@ def __str__(self): return f'{self.product}, {self.quantity}' +class OrderQuerySet(models.QuerySet): + def with_totals(self): + return self.annotate(total=F('products_ordered__product_price') * F('products_ordered__quantity')) + + class Order(models.Model): + + class Status(models.IntegerChoices): + NEW = 0, 'Не обработан' + PREPARING = 1, 'Готовится' + DELIVERING = 2, 'В пути' + COMPLETE = 3, 'Доставлен' + firstname = models.CharField( 'имя', max_length=50, @@ -182,6 +189,12 @@ class Order(models.Model): 'время создания', auto_now=True, ) + status = models.SmallIntegerField( + 'статус', + choices=Status.choices, + default=Status.NEW, + db_index=True, + ) objects = OrderQuerySet.as_manager() diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index 4b4a8c3e1..a17387103 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -14,6 +14,7 @@

Необработанные заказы

+ @@ -24,6 +25,7 @@

Необработанные заказы

{% for order in orders %} + diff --git a/restaurateur/views.py b/restaurateur/views.py index 02b01982c..dd1412e8d 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -93,5 +93,5 @@ def view_restaurants(request): def view_orders(request): return render(request, template_name='order_items.html', context={ - 'orders': Order.objects.with_totals() + 'orders': Order.objects.filter(status=Order.Status.NEW).with_totals() }) From fbc823c888a43153ef1d478e1dd0c257d062cee1 Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 11 Mar 2023 23:49:08 +0300 Subject: [PATCH 14/67] fix duplicating orders while annotating --- foodcartapp/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/foodcartapp/models.py b/foodcartapp/models.py index 3008317bf..e48b8a1a4 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -1,6 +1,6 @@ from django.core.validators import MinValueValidator from django.db import models -from django.db.models import F +from django.db.models import F, Sum from phonenumber_field.modelfields import PhoneNumberField @@ -151,7 +151,7 @@ def __str__(self): class OrderQuerySet(models.QuerySet): def with_totals(self): - return self.annotate(total=F('products_ordered__product_price') * F('products_ordered__quantity')) + return self.annotate(total=Sum(F('products_ordered__product_price') * F('products_ordered__quantity'))) class Order(models.Model): @@ -203,4 +203,4 @@ class Meta: verbose_name_plural = 'заказы' def __str__(self): - return f'{self.firstname} {self.lastname[:1]}., {self.created_at:%d.%m.%y %H:%M:%S}' + return f'{self.id}: {self.firstname} {self.lastname[:1]}., {self.created_at:%d.%m.%y %H:%M:%S}' From fd4a2ea4b72cccc0bc9454de495e90d30eb7b4d5 Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 11 Mar 2023 23:49:18 +0300 Subject: [PATCH 15/67] add comments to orders --- foodcartapp/models.py | 4 ++++ foodcartapp/views.py | 1 + restaurateur/templates/order_items.html | 2 ++ restaurateur/views.py | 1 - 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/foodcartapp/models.py b/foodcartapp/models.py index e48b8a1a4..ca4811005 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -195,6 +195,10 @@ class Status(models.IntegerChoices): default=Status.NEW, db_index=True, ) + comment = models.TextField( + 'комментарий', + blank=True, + ) objects = OrderQuerySet.as_manager() diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 4e2c9981e..757cfe53e 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -7,6 +7,7 @@ from .models import Product, Order, ProductOrder + def banners_list_api(request): # FIXME move data to db? return JsonResponse([ diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index a17387103..1a80f7f9b 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -19,6 +19,7 @@

Необработанные заказы

+ @@ -30,6 +31,7 @@

Необработанные заказы

+ {% endfor %} diff --git a/restaurateur/views.py b/restaurateur/views.py index dd1412e8d..b0ab5c1f0 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -91,7 +91,6 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): - return render(request, template_name='order_items.html', context={ 'orders': Order.objects.filter(status=Order.Status.NEW).with_totals() }) From 340459ec99132cf960327a590c67243fc71f09f3 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 12 Mar 2023 00:08:18 +0300 Subject: [PATCH 16/67] add times to orders --- foodcartapp/admin.py | 2 ++ foodcartapp/migrations/0043_order_comment.py | 18 ++++++++++++ .../migrations/0044_auto_20230311_2052.py | 28 +++++++++++++++++++ foodcartapp/models.py | 21 +++++++++++--- restaurateur/templates/order_items.html | 7 ++++- star_burger/settings.py | 1 + 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 foodcartapp/migrations/0043_order_comment.py create mode 100644 foodcartapp/migrations/0044_auto_20230311_2052.py diff --git a/foodcartapp/admin.py b/foodcartapp/admin.py index 6d9378d51..170c6d2fc 100644 --- a/foodcartapp/admin.py +++ b/foodcartapp/admin.py @@ -111,6 +111,7 @@ def get_image_list_preview(self, obj): class ProductAdmin(admin.ModelAdmin): pass + class ProductOrderInlineForm(ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -127,6 +128,7 @@ class ProductOrderInline(admin.TabularInline): class OrderAdmin(admin.ModelAdmin): inlines = [ProductOrderInline] list_display = ('firstname', 'lastname', 'phonenumber', 'created_at') + readonly_fields = ('created_at',) def save_formset(self, request, form, formset, change): instances = formset.save(commit=False) diff --git a/foodcartapp/migrations/0043_order_comment.py b/foodcartapp/migrations/0043_order_comment.py new file mode 100644 index 000000000..b2c998341 --- /dev/null +++ b/foodcartapp/migrations/0043_order_comment.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.15 on 2023-03-11 20:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0042_alter_order_status'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='comment', + field=models.TextField(blank=True, verbose_name='комментарий'), + ), + ] diff --git a/foodcartapp/migrations/0044_auto_20230311_2052.py b/foodcartapp/migrations/0044_auto_20230311_2052.py new file mode 100644 index 000000000..de7821b36 --- /dev/null +++ b/foodcartapp/migrations/0044_auto_20230311_2052.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.15 on 2023-03-11 20:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0043_order_comment'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='called_at', + field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='время звонка'), + ), + migrations.AddField( + model_name='order', + name='delivered_at', + field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='когда доставлен'), + ), + migrations.AlterField( + model_name='order', + name='created_at', + field=models.DateTimeField(auto_now=True, db_index=True, verbose_name='время создания'), + ), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index ca4811005..afcf9da2b 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -185,10 +185,6 @@ class Status(models.IntegerChoices): related_name='order', through='ProductOrder', ) - created_at = models.DateTimeField( - 'время создания', - auto_now=True, - ) status = models.SmallIntegerField( 'статус', choices=Status.choices, @@ -199,6 +195,23 @@ class Status(models.IntegerChoices): 'комментарий', blank=True, ) + created_at = models.DateTimeField( + 'время создания', + auto_now_add=True, + db_index=True, + ) + called_at = models.DateTimeField( + 'время звонка', + blank=True, + null=True, + db_index=True, + ) + delivered_at = models.DateTimeField( + 'когда доставлен', + blank=True, + null=True, + db_index=True, + ) objects = OrderQuerySet.as_manager() diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index 1a80f7f9b..2ffa63b93 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -1,4 +1,5 @@ {% extends 'base_restaurateur_page.html' %} +{% load humanize %} {% block title %}Необработанные заказы | Star Burger{% endblock %} @@ -15,6 +16,7 @@

Необработанные заказы

+ @@ -27,12 +29,15 @@

Необработанные заказы

+ - + {% endfor %}
ID заказаСтатус Стоимость Клиент Телефон
{{ order.id }}{{ order.get_status_display }} {{ order.total|floatformat:"2g" }} ₽ {{ order.firstname }} {{ order.lastname }} {{ order.phonenumber }}Клиент Телефон Адрес доставкиКомментарии Ссылка на админку
{{ order.firstname }} {{ order.lastname }} {{ order.phonenumber }} {{ order.address }}{{ order.comment }} Редактировать
ID заказа СтатусВремя создания Стоимость Клиент Телефон
{{ order.id }} {{ order.get_status_display }}{{ order.created_at|naturaltime }} {{ order.total|floatformat:"2g" }} ₽ {{ order.firstname }} {{ order.lastname }} {{ order.phonenumber }} {{ order.address }} {{ order.comment }}Редактировать + Редактировать +
diff --git a/star_burger/settings.py b/star_burger/settings.py index 9d11fc1d1..db30c7a69 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -26,6 +26,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.humanize', 'debug_toolbar', 'phonenumber_field', 'rest_framework', From 1c94521e87286083b7a30739889764d0bdbf784f Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 12 Mar 2023 00:28:29 +0300 Subject: [PATCH 17/67] add payment method to orders --- .../migrations/0045_auto_20230311_2125.py | 23 +++++++++++++++++++ foodcartapp/models.py | 13 +++++++++++ restaurateur/templates/order_items.html | 2 ++ 3 files changed, 38 insertions(+) create mode 100644 foodcartapp/migrations/0045_auto_20230311_2125.py diff --git a/foodcartapp/migrations/0045_auto_20230311_2125.py b/foodcartapp/migrations/0045_auto_20230311_2125.py new file mode 100644 index 000000000..18b4b8cdc --- /dev/null +++ b/foodcartapp/migrations/0045_auto_20230311_2125.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.15 on 2023-03-11 21:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0044_auto_20230311_2052'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='payment_method', + field=models.CharField(blank=True, choices=[('CASH', 'Наличные'), ('CARD', 'Карта')], db_index=True, max_length=4, null=True, verbose_name='способ оплаты'), + ), + migrations.AlterField( + model_name='order', + name='created_at', + field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='время создания'), + ), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index afcf9da2b..e47e9ffe9 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -162,6 +162,11 @@ class Status(models.IntegerChoices): DELIVERING = 2, 'В пути' COMPLETE = 3, 'Доставлен' + class PaymentMethod(models.TextChoices): + CASH = 'CASH', 'Наличные' + CARD = 'CARD', 'Карта' + __empty__ = '' + firstname = models.CharField( 'имя', max_length=50, @@ -212,6 +217,14 @@ class Status(models.IntegerChoices): null=True, db_index=True, ) + payment_method = models.CharField( + 'способ оплаты', + choices=PaymentMethod.choices, + max_length=4, + blank=True, + null=True, + db_index=True, + ) objects = OrderQuerySet.as_manager() diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index 2ffa63b93..23a96de61 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -18,6 +18,7 @@

Необработанные заказы

Статус Время создания Стоимость + Способ оплаты Клиент Телефон Адрес доставки @@ -31,6 +32,7 @@

Необработанные заказы

{{ order.get_status_display }} {{ order.created_at|naturaltime }} {{ order.total|floatformat:"2g" }} ₽ + {{ order.get_payment_method_display }} {{ order.firstname }} {{ order.lastname }} {{ order.phonenumber }} {{ order.address }} From aa373a9751cc55ca15ccafedcabd423aef72dcd9 Mon Sep 17 00:00:00 2001 From: mavel Date: Mon, 13 Mar 2023 01:37:25 +0300 Subject: [PATCH 18/67] add restaurant to orders --- .../migrations/0046_auto_20230312_1642.py | 24 +++++++++++++++++++ foodcartapp/models.py | 23 ++++++++++++++++++ restaurateur/templates/order_items.html | 14 +++++++++++ restaurateur/views.py | 2 +- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 foodcartapp/migrations/0046_auto_20230312_1642.py diff --git a/foodcartapp/migrations/0046_auto_20230312_1642.py b/foodcartapp/migrations/0046_auto_20230312_1642.py new file mode 100644 index 000000000..bb6d3c124 --- /dev/null +++ b/foodcartapp/migrations/0046_auto_20230312_1642.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.15 on 2023-03-12 16:42 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0045_auto_20230311_2125'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='restaurant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='foodcartapp.restaurant', verbose_name='ответственный ресторан'), + ), + migrations.AlterField( + model_name='order', + name='payment_method', + field=models.CharField(blank=True, choices=[(None, ''), ('CASH', 'Наличные'), ('CARD', 'Карта')], db_index=True, max_length=4, null=True, verbose_name='способ оплаты'), + ), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index e47e9ffe9..661c2dfd9 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -1,3 +1,5 @@ +from collections import defaultdict + from django.core.validators import MinValueValidator from django.db import models from django.db.models import F, Sum @@ -225,6 +227,14 @@ class PaymentMethod(models.TextChoices): null=True, db_index=True, ) + restaurant = models.ForeignKey( + 'Restaurant', + verbose_name='ответственный ресторан', + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name='orders', + ) objects = OrderQuerySet.as_manager() @@ -234,3 +244,16 @@ class Meta: def __str__(self): return f'{self.id}: {self.firstname} {self.lastname[:1]}., {self.created_at:%d.%m.%y %H:%M:%S}' + + def available_restaurants(self): + ordered_products = self.products.all() + products_in_restaurants = ( + RestaurantMenuItem.objects + .filter(product__in=ordered_products, availability=True) + .values_list('product', 'restaurant') + ) + restaurants_by_product = defaultdict(set) + for product, restaurant in products_in_restaurants: + restaurants_by_product[product].add(restaurant) + available_restaurant_ids = set.intersection(*restaurants_by_product.values()) + return Restaurant.objects.filter(pk__in=available_restaurant_ids) diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index 23a96de61..f139a2e6e 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -23,6 +23,7 @@

Необработанные заказы

Телефон Адрес доставки Комментарии + Рестораны Ссылка на админку @@ -37,6 +38,19 @@

Необработанные заказы

{{ order.phonenumber }} {{ order.address }} {{ order.comment }} + + {% if order.status == order.Status.NEW %} +
+ Могут приготовить: + {% for rest in order.available_restaurants %} +
  • {{ rest }}
  • + {% endfor %} +
    + {% else %} + Передан в ресторан: + {{ order.restaurant }} + {% endif %} + Редактировать diff --git a/restaurateur/views.py b/restaurateur/views.py index b0ab5c1f0..3db014760 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -92,5 +92,5 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): return render(request, template_name='order_items.html', context={ - 'orders': Order.objects.filter(status=Order.Status.NEW).with_totals() + 'orders': Order.objects.exclude(status=Order.Status.COMPLETE).with_totals() }) From c713dbee6371c994ffb546c7c0087c2fefe9be4f Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Apr 2023 18:38:50 +0300 Subject: [PATCH 19/67] add available restaurants to orders view --- foodcartapp/models.py | 13 ------------- restaurateur/views.py | 20 +++++++++++++++++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/foodcartapp/models.py b/foodcartapp/models.py index 661c2dfd9..f1f79ff05 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -244,16 +244,3 @@ class Meta: def __str__(self): return f'{self.id}: {self.firstname} {self.lastname[:1]}., {self.created_at:%d.%m.%y %H:%M:%S}' - - def available_restaurants(self): - ordered_products = self.products.all() - products_in_restaurants = ( - RestaurantMenuItem.objects - .filter(product__in=ordered_products, availability=True) - .values_list('product', 'restaurant') - ) - restaurants_by_product = defaultdict(set) - for product, restaurant in products_in_restaurants: - restaurants_by_product[product].add(restaurant) - available_restaurant_ids = set.intersection(*restaurants_by_product.values()) - return Restaurant.objects.filter(pk__in=available_restaurant_ids) diff --git a/restaurateur/views.py b/restaurateur/views.py index 3db014760..25162368c 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -1,13 +1,14 @@ +from collections import defaultdict + from django import forms from django.shortcuts import redirect, render from django.views import View from django.urls import reverse_lazy from django.contrib.auth.decorators import user_passes_test - from django.contrib.auth import authenticate, login from django.contrib.auth import views as auth_views -from foodcartapp.models import Product, Restaurant, Order +from foodcartapp.models import Product, Restaurant, Order, ProductOrder, RestaurantMenuItem class Login(forms.Form): @@ -91,6 +92,19 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): + orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals() + ordered_products = ProductOrder.objects.filter(order__in=orders).prefetch_related('product', 'order') + products_in_restaurants = ( + RestaurantMenuItem.objects + .filter(product__in=[order.product for order in ordered_products], availability=True) + .prefetch_related('restaurant', 'product') + ) + for order in orders: + required_product_ids = [op.product.id for op in ordered_products if op.order == order] + order.available_restaurants = { + pr.restaurant for pr in products_in_restaurants if pr.product.id in required_product_ids + } + return render(request, template_name='order_items.html', context={ - 'orders': Order.objects.exclude(status=Order.Status.COMPLETE).with_totals() + 'orders': orders, }) From 62bcf3fb175d174d3b49d8716d2d1e2be54849f3 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Apr 2023 22:13:43 +0300 Subject: [PATCH 20/67] add distance to available restaurants --- README.md | 8 +++++-- requirements.txt | 1 + restaurateur/templates/order_items.html | 7 +++++- restaurateur/views.py | 32 ++++++++++++++++++++++--- star_burger/settings.py | 1 + 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 54bf1453f..60d4e5059 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ python --version ``` **Важно!** Версия Python должна быть не ниже 3.6. -Возможно, вместо команды `python` здесь и в остальных инструкциях этого README придётся использовать `python3`. Зависит это от операционной системы и от того, установлен ли у вас Python старой второй версии. +Возможно, вместо команды `python` здесь и в остальных инструкциях этого README придётся использовать `python3`. Зависит это от операционной системы и от того, установлен ли у вас Python старой второй версии. В каталоге проекта создайте виртуальное окружение: ```sh @@ -54,11 +54,15 @@ python -m venv venv pip install -r requirements.txt ``` -Определите переменную окружения `SECRET_KEY`. Создать файл `.env` в каталоге `star_burger/` и положите туда такой код: +Определите переменную окружения `SECRET_KEY`. Создайте файл `.env` в каталоге `star_burger/` и положите туда такой код: ```sh SECRET_KEY=django-insecure-0if40nf4nf93n4 ``` +Получите API-ключ для +[Яндекс-геокодера](https://developer.tech.yandex.ru/services/) (подробнее [здесь](https://dvmn.org/encyclopedia/api-docs/yandex-geocoder-api/)). +Положите ключ в переменную `YANDEX_GEO_KEY` в файле `.env`. + Создайте файл базы данных SQLite и отмигрируйте её следующей командой: ```sh diff --git a/requirements.txt b/requirements.txt index de403e5aa..b5cf774ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ environs[django]==9.3.2 wheel==0.38.4 django-phonenumber-field[phonenumbers]==7.0.2 djangorestframework==3.14.0 +geopy~=2.3.0 diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index f139a2e6e..a74ae11f9 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -43,7 +43,12 @@

    Необработанные заказы

    Могут приготовить: {% for rest in order.available_restaurants %} -
  • {{ rest }}
  • +
  • {{ rest }}: + {% if rest.distance %} + {{ rest.distance|floatformat:'2g' }} км
  • + {% else %} + Расстояние недоступно + {% endif %} {% endfor %}
    {% else %} diff --git a/restaurateur/views.py b/restaurateur/views.py index 25162368c..a34927752 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -1,12 +1,15 @@ -from collections import defaultdict +from copy import copy +import geopy.exc from django import forms from django.shortcuts import redirect, render from django.views import View from django.urls import reverse_lazy +from django.conf import settings from django.contrib.auth.decorators import user_passes_test from django.contrib.auth import authenticate, login from django.contrib.auth import views as auth_views +from geopy import geocoders, distance from foodcartapp.models import Product, Restaurant, Order, ProductOrder, RestaurantMenuItem @@ -92,18 +95,41 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): - orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals() + geocoder = geocoders.Yandex(api_key=settings.YANDEX_GEO_KEY) + orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals().prefetch_related('restaurant') ordered_products = ProductOrder.objects.filter(order__in=orders).prefetch_related('product', 'order') products_in_restaurants = ( RestaurantMenuItem.objects .filter(product__in=[order.product for order in ordered_products], availability=True) .prefetch_related('restaurant', 'product') ) + for pr in products_in_restaurants: + try: + pr.restaurant.location = geocoder.geocode(pr.restaurant.address) + except geopy.exc.GeocoderServiceError: + pr.restaurant.location = None + for order in orders: + try: + order.location = geocoder.geocode(order.address) + except geopy.exc.GeocoderServiceError: + order.location = None required_product_ids = [op.product.id for op in ordered_products if op.order == order] order.available_restaurants = { - pr.restaurant for pr in products_in_restaurants if pr.product.id in required_product_ids + copy(pr.restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids } + for restaurant in order.available_restaurants: + try: + restaurant.distance = distance.distance( + (order.location.latitude, order.location.longitude), + (restaurant.location.latitude, restaurant.location.longitude), + ).km + except AttributeError: + restaurant.distance = None + order.available_restaurants = sorted( + order.available_restaurants, + key=lambda rest: rest.distance, + ) return render(request, template_name='order_items.html', context={ 'orders': orders, diff --git a/star_burger/settings.py b/star_burger/settings.py index db30c7a69..74a3aff2a 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -128,3 +128,4 @@ PHONENUMBER_DEFAULT_REGION = 'RU' +YANDEX_GEO_KEY = env.str('YANDEX_GEO_KEY') From 56e2c7302204e847f596affcf816c39c0ed0d185 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Apr 2023 23:06:18 +0300 Subject: [PATCH 21/67] create Location model --- foodcartapp/admin.py | 19 +++++++++++++-- foodcartapp/views.py | 2 ++ places/__init__.py | 0 places/admin.py | 3 +++ places/apps.py | 6 +++++ places/migrations/0001_initial.py | 24 +++++++++++++++++++ places/migrations/__init__.py | 0 places/models.py | 40 +++++++++++++++++++++++++++++++ places/tests.py | 3 +++ places/views.py | 3 +++ restaurateur/views.py | 16 ++++++------- star_burger/settings.py | 1 + 12 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 places/__init__.py create mode 100644 places/admin.py create mode 100644 places/apps.py create mode 100644 places/migrations/0001_initial.py create mode 100644 places/migrations/__init__.py create mode 100644 places/models.py create mode 100644 places/tests.py create mode 100644 places/views.py diff --git a/foodcartapp/admin.py b/foodcartapp/admin.py index 170c6d2fc..fae7ddcbc 100644 --- a/foodcartapp/admin.py +++ b/foodcartapp/admin.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib import admin from django.forms import ModelForm from django.http import HttpResponseRedirect @@ -12,7 +13,13 @@ from .models import RestaurantMenuItem from .models import Order from .models import ProductOrder -from star_burger.settings import ALLOWED_HOSTS +from places.models import Location + + +@admin.register(Location) +class LocationAdmin(admin.ModelAdmin): + list_display = ['address', 'latitude', 'longitude', 'updated_at'] + readonly_fields = ['updated_at'] class RestaurantMenuItemInline(admin.TabularInline): @@ -36,6 +43,10 @@ class RestaurantAdmin(admin.ModelAdmin): RestaurantMenuItemInline ] + def save_model(self, request, obj, form, change): + super().save_model(request, obj, form, change) + Location.update_by_address(obj.address) + @admin.register(Product) class ProductAdmin(admin.ModelAdmin): @@ -130,6 +141,10 @@ class OrderAdmin(admin.ModelAdmin): list_display = ('firstname', 'lastname', 'phonenumber', 'created_at') readonly_fields = ('created_at',) + def save_model(self, request, obj, form, change): + super().save_model(request, obj, form, change) + Location.update_by_address(obj.address) + def save_formset(self, request, form, formset, change): instances = formset.save(commit=False) for obj in formset.deleted_objects: @@ -143,7 +158,7 @@ def save_formset(self, request, form, formset, change): def response_change(self, request, obj): response = super().response_change(request, obj) if "next" in request.GET: - if url_has_allowed_host_and_scheme(request.GET['next'], ALLOWED_HOSTS): + if url_has_allowed_host_and_scheme(request.GET['next'], settings.ALLOWED_HOSTS): return HttpResponseRedirect(request.GET['next']) else: return response diff --git a/foodcartapp/views.py b/foodcartapp/views.py index 757cfe53e..eb85d4c8d 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -6,6 +6,7 @@ from rest_framework.serializers import ModelSerializer from .models import Product, Order, ProductOrder +from places.models import Location def banners_list_api(request): @@ -95,4 +96,5 @@ def register_order(request): 'product_price': product.price, } ) + Location.update_by_address(order.address) return Response(serializer.data) diff --git a/places/__init__.py b/places/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/places/admin.py b/places/admin.py new file mode 100644 index 000000000..8c38f3f3d --- /dev/null +++ b/places/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/places/apps.py b/places/apps.py new file mode 100644 index 000000000..b3d0c3d3f --- /dev/null +++ b/places/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PlacesConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'places' diff --git a/places/migrations/0001_initial.py b/places/migrations/0001_initial.py new file mode 100644 index 000000000..6d3d91566 --- /dev/null +++ b/places/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.15 on 2023-04-02 19:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Location', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('address', models.CharField(max_length=200, unique=True, verbose_name='адрес')), + ('latitude', models.FloatField(verbose_name='широта')), + ('longitude', models.FloatField(verbose_name='долгота')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='обновлено')), + ], + ), + ] diff --git a/places/migrations/__init__.py b/places/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/places/models.py b/places/models.py new file mode 100644 index 000000000..d42fc8429 --- /dev/null +++ b/places/models.py @@ -0,0 +1,40 @@ +import geopy.exc +from django.conf import settings +from django.db import models +from geopy import geocoders + + +class Location(models.Model): + address = models.CharField( + 'адрес', + max_length=200, + unique=True, + ) + latitude = models.FloatField( + 'широта' + ) + longitude = models.FloatField( + 'долгота' + ) + updated_at = models.DateTimeField( + 'обновлено', + auto_now=True, + ) + + def __str__(self): + return self.address + + @classmethod + def update_by_address(cls, address): + try: + geocoder = geocoders.Yandex(api_key=settings.YANDEX_GEO_KEY) + geo = geocoder.geocode(address) + cls.objects.update_or_create( + address=address, + defaults={ + 'latitude': geo.latitude, + 'longitude': geo.longitude, + }, + ) + except (geopy.exc.GeocoderServiceError, AttributeError): + pass diff --git a/places/tests.py b/places/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/places/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/places/views.py b/places/views.py new file mode 100644 index 000000000..91ea44a21 --- /dev/null +++ b/places/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/restaurateur/views.py b/restaurateur/views.py index a34927752..4fc580412 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -1,17 +1,16 @@ from copy import copy -import geopy.exc from django import forms from django.shortcuts import redirect, render from django.views import View from django.urls import reverse_lazy -from django.conf import settings from django.contrib.auth.decorators import user_passes_test from django.contrib.auth import authenticate, login from django.contrib.auth import views as auth_views -from geopy import geocoders, distance +from geopy import distance from foodcartapp.models import Product, Restaurant, Order, ProductOrder, RestaurantMenuItem +from places.models import Location class Login(forms.Form): @@ -95,7 +94,6 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): - geocoder = geocoders.Yandex(api_key=settings.YANDEX_GEO_KEY) orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals().prefetch_related('restaurant') ordered_products = ProductOrder.objects.filter(order__in=orders).prefetch_related('product', 'order') products_in_restaurants = ( @@ -105,14 +103,14 @@ def view_orders(request): ) for pr in products_in_restaurants: try: - pr.restaurant.location = geocoder.geocode(pr.restaurant.address) - except geopy.exc.GeocoderServiceError: + pr.restaurant.location = Location.objects.get(address=pr.restaurant.address) + except Location.DoesNotExist: pr.restaurant.location = None for order in orders: try: - order.location = geocoder.geocode(order.address) - except geopy.exc.GeocoderServiceError: + order.location = Location.objects.get(address=order.address) + except Location.DoesNotExist: order.location = None required_product_ids = [op.product.id for op in ordered_products if op.order == order] order.available_restaurants = { @@ -128,7 +126,7 @@ def view_orders(request): restaurant.distance = None order.available_restaurants = sorted( order.available_restaurants, - key=lambda rest: rest.distance, + key=lambda rest: rest.distance or 0, ) return render(request, template_name='order_items.html', context={ diff --git a/star_burger/settings.py b/star_burger/settings.py index 74a3aff2a..1f76e80d9 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -20,6 +20,7 @@ INSTALLED_APPS = [ 'foodcartapp.apps.FoodcartappConfig', 'restaurateur.apps.RestaurateurConfig', + 'places.apps.PlacesConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', From c3aa387dfba4a9d4c4568ff258ae75faaa01e493 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Apr 2023 23:28:59 +0300 Subject: [PATCH 22/67] optimize SQL queries --- restaurateur/views.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/restaurateur/views.py b/restaurateur/views.py index 4fc580412..510b5eeea 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -101,17 +101,18 @@ def view_orders(request): .filter(product__in=[order.product for order in ordered_products], availability=True) .prefetch_related('restaurant', 'product') ) + + addresses = [order.address for order in orders] + [pr.restaurant.address for pr in products_in_restaurants] + locations = Location.objects.filter(address__in=addresses) + locations_by_address = {address: None for address in addresses} + for location in locations: + locations_by_address[location.address] = location + for pr in products_in_restaurants: - try: - pr.restaurant.location = Location.objects.get(address=pr.restaurant.address) - except Location.DoesNotExist: - pr.restaurant.location = None + pr.restaurant.location = locations_by_address.get(pr.restaurant.address) for order in orders: - try: - order.location = Location.objects.get(address=order.address) - except Location.DoesNotExist: - order.location = None + order.location = locations_by_address.get(order.address) required_product_ids = [op.product.id for op in ordered_products if op.order == order] order.available_restaurants = { copy(pr.restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids From 59443fc0795d2fac5768f5dc32fe1ceb74dbe077 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 14:41:27 +0300 Subject: [PATCH 23/67] merge statements --- foodcartapp/admin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/foodcartapp/admin.py b/foodcartapp/admin.py index fae7ddcbc..d9278aa58 100644 --- a/foodcartapp/admin.py +++ b/foodcartapp/admin.py @@ -157,8 +157,7 @@ def save_formset(self, request, form, formset, change): def response_change(self, request, obj): response = super().response_change(request, obj) - if "next" in request.GET: - if url_has_allowed_host_and_scheme(request.GET['next'], settings.ALLOWED_HOSTS): - return HttpResponseRedirect(request.GET['next']) + if url_has_allowed_host_and_scheme(request.GET.get('next'), settings.ALLOWED_HOSTS): + return HttpResponseRedirect(request.GET['next']) else: return response From 196838862ad7080beff80e61f5b17ce9e117e169 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 14:41:36 +0300 Subject: [PATCH 24/67] rename an attribute --- ...ame_restaurant_order_assigned_restaurant.py | 18 ++++++++++++++++++ foodcartapp/models.py | 2 +- restaurateur/views.py | 8 ++++---- 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 foodcartapp/migrations/0047_rename_restaurant_order_assigned_restaurant.py diff --git a/foodcartapp/migrations/0047_rename_restaurant_order_assigned_restaurant.py b/foodcartapp/migrations/0047_rename_restaurant_order_assigned_restaurant.py new file mode 100644 index 000000000..9a5ef8b2c --- /dev/null +++ b/foodcartapp/migrations/0047_rename_restaurant_order_assigned_restaurant.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.15 on 2023-04-09 11:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0046_auto_20230312_1642'), + ] + + operations = [ + migrations.RenameField( + model_name='order', + old_name='restaurant', + new_name='assigned_restaurant', + ), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index f1f79ff05..93e919cf7 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -227,7 +227,7 @@ class PaymentMethod(models.TextChoices): null=True, db_index=True, ) - restaurant = models.ForeignKey( + assigned_restaurant = models.ForeignKey( 'Restaurant', verbose_name='ответственный ресторан', on_delete=models.SET_NULL, diff --git a/restaurateur/views.py b/restaurateur/views.py index 510b5eeea..cd7926336 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -94,7 +94,7 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): - orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals().prefetch_related('restaurant') + orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals().prefetch_related('assigned_restaurant') ordered_products = ProductOrder.objects.filter(order__in=orders).prefetch_related('product', 'order') products_in_restaurants = ( RestaurantMenuItem.objects @@ -102,20 +102,20 @@ def view_orders(request): .prefetch_related('restaurant', 'product') ) - addresses = [order.address for order in orders] + [pr.restaurant.address for pr in products_in_restaurants] + addresses = [order.address for order in orders] + [pr.assigned_restaurant.address for pr in products_in_restaurants] locations = Location.objects.filter(address__in=addresses) locations_by_address = {address: None for address in addresses} for location in locations: locations_by_address[location.address] = location for pr in products_in_restaurants: - pr.restaurant.location = locations_by_address.get(pr.restaurant.address) + pr.assigned_restaurant.location = locations_by_address.get(pr.assigned_restaurant.address) for order in orders: order.location = locations_by_address.get(order.address) required_product_ids = [op.product.id for op in ordered_products if op.order == order] order.available_restaurants = { - copy(pr.restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids + copy(pr.assigned_restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids } for restaurant in order.available_restaurants: try: From b58b021a6c86ca894a8316747eef47f2bbb64002 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 14:47:36 +0300 Subject: [PATCH 25/67] add qty limits to order --- .../0048_alter_productorder_quantity.py | 19 +++++++++++++++++++ foodcartapp/models.py | 10 ++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 foodcartapp/migrations/0048_alter_productorder_quantity.py diff --git a/foodcartapp/migrations/0048_alter_productorder_quantity.py b/foodcartapp/migrations/0048_alter_productorder_quantity.py new file mode 100644 index 000000000..2521a6443 --- /dev/null +++ b/foodcartapp/migrations/0048_alter_productorder_quantity.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.15 on 2023-04-09 11:47 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('foodcartapp', '0047_rename_restaurant_order_assigned_restaurant'), + ] + + operations = [ + migrations.AlterField( + model_name='productorder', + name='quantity', + field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1000)], verbose_name='количество'), + ), + ] diff --git a/foodcartapp/models.py b/foodcartapp/models.py index 93e919cf7..fffea8e6d 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -1,6 +1,6 @@ from collections import defaultdict -from django.core.validators import MinValueValidator +from django.core.validators import MinValueValidator, MaxValueValidator from django.db import models from django.db.models import F, Sum from phonenumber_field.modelfields import PhoneNumberField @@ -138,7 +138,13 @@ class ProductOrder(models.Model): on_delete=models.CASCADE, related_name='products_ordered' ) - quantity = models.PositiveIntegerField('количество') + quantity = models.PositiveIntegerField( + 'количество', + validators=[ + MinValueValidator(1), + MaxValueValidator(1000), + ] + ) product_price = models.DecimalField( 'цена товара', max_digits=8, From 10258cedbc3a85cd9ba62cd69a7dbdc66c3597cb Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 14:53:38 +0300 Subject: [PATCH 26/67] move serializers to dedicated file --- foodcartapp/serializers.py | 17 +++++++++++++++++ foodcartapp/views.py | 18 ++---------------- 2 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 foodcartapp/serializers.py diff --git a/foodcartapp/serializers.py b/foodcartapp/serializers.py new file mode 100644 index 000000000..d123896f6 --- /dev/null +++ b/foodcartapp/serializers.py @@ -0,0 +1,17 @@ +from rest_framework.serializers import ModelSerializer + +from .models import Order, ProductOrder + + +class ProductOrderSerializer(ModelSerializer): + class Meta: + model = ProductOrder + fields = ['product', 'quantity'] + + +class OrderSerializer(ModelSerializer): + products = ProductOrderSerializer(many=True, allow_empty=False) + + class Meta: + model = Order + fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products'] diff --git a/foodcartapp/views.py b/foodcartapp/views.py index eb85d4c8d..ab4c5a1ab 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -3,9 +3,9 @@ from django.templatetags.static import static from rest_framework.decorators import api_view from rest_framework.response import Response -from rest_framework.serializers import ModelSerializer -from .models import Product, Order, ProductOrder +from .models import Product, Order +from .serializers import OrderSerializer from places.models import Location @@ -61,20 +61,6 @@ def product_list_api(request): }) -class ProductOrderSerializer(ModelSerializer): - class Meta: - model = ProductOrder - fields = ['product', 'quantity'] - - -class OrderSerializer(ModelSerializer): - products = ProductOrderSerializer(many=True, allow_empty=False) - - class Meta: - model = Order - fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products'] - - @transaction.atomic @api_view(['POST']) def register_order(request): From a42f3e5209cacc33608431eb17bf1f8ee31a6884 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 15:22:02 +0300 Subject: [PATCH 27/67] fix renaming attribute --- restaurateur/templates/order_items.html | 2 +- restaurateur/views.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html index a74ae11f9..0cbd73931 100644 --- a/restaurateur/templates/order_items.html +++ b/restaurateur/templates/order_items.html @@ -53,7 +53,7 @@

    Необработанные заказы

    {% else %} Передан в ресторан: - {{ order.restaurant }} + {{ order.assigned_restaurant }} {% endif %} diff --git a/restaurateur/views.py b/restaurateur/views.py index cd7926336..e111e246a 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -102,20 +102,20 @@ def view_orders(request): .prefetch_related('restaurant', 'product') ) - addresses = [order.address for order in orders] + [pr.assigned_restaurant.address for pr in products_in_restaurants] + addresses = [order.address for order in orders] + [pr.restaurant.address for pr in products_in_restaurants] locations = Location.objects.filter(address__in=addresses) locations_by_address = {address: None for address in addresses} for location in locations: locations_by_address[location.address] = location for pr in products_in_restaurants: - pr.assigned_restaurant.location = locations_by_address.get(pr.assigned_restaurant.address) + pr.restaurant.location = locations_by_address.get(pr.restaurant.address) for order in orders: order.location = locations_by_address.get(order.address) required_product_ids = [op.product.id for op in ordered_products if op.order == order] order.available_restaurants = { - copy(pr.assigned_restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids + copy(pr.restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids } for restaurant in order.available_restaurants: try: From 5b7d640807799e9d7aee851ee73c0ed894817548 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 15:23:19 +0300 Subject: [PATCH 28/67] move instance saving code into serializer --- foodcartapp/serializers.py | 23 ++++++++++++++++++++++- foodcartapp/views.py | 18 +----------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/foodcartapp/serializers.py b/foodcartapp/serializers.py index d123896f6..07285c2b2 100644 --- a/foodcartapp/serializers.py +++ b/foodcartapp/serializers.py @@ -1,5 +1,6 @@ from rest_framework.serializers import ModelSerializer +from places.models import Location from .models import Order, ProductOrder @@ -10,8 +11,28 @@ class Meta: class OrderSerializer(ModelSerializer): - products = ProductOrderSerializer(many=True, allow_empty=False) + products = ProductOrderSerializer(many=True, allow_empty=False, write_only=True) class Meta: model = Order fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products'] + + def create(self, validated_data): + products_in_order = self.validated_data['products'] + order = Order.objects.create( + firstname=self.validated_data['firstname'], + lastname=self.validated_data['lastname'], + phonenumber=self.validated_data['phonenumber'], + address=self.validated_data['address'], + ) + for product_order in products_in_order: + product = product_order['product'] + order.products.add( + product, + through_defaults={ + 'quantity': product_order['quantity'], + 'product_price': product.price, + } + ) + Location.update_by_address(order.address) + return order diff --git a/foodcartapp/views.py b/foodcartapp/views.py index ab4c5a1ab..be6ae3fd6 100644 --- a/foodcartapp/views.py +++ b/foodcartapp/views.py @@ -66,21 +66,5 @@ def product_list_api(request): def register_order(request): serializer = OrderSerializer(data=request.data) serializer.is_valid(raise_exception=True) - products_in_order = serializer.validated_data['products'] - order = Order.objects.create( - firstname=serializer.validated_data['firstname'], - lastname=serializer.validated_data['lastname'], - phonenumber=serializer.validated_data['phonenumber'], - address=serializer.validated_data['address'], - ) - for product_order in products_in_order: - product = product_order['product'] - order.products.add( - product, - through_defaults={ - 'quantity': product_order['quantity'], - 'product_price': product.price, - } - ) - Location.update_by_address(order.address) + serializer.save() return Response(serializer.data) From e51bdc18a285e8b416f35109f14b2e7289e5e8ea Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 15:25:10 +0300 Subject: [PATCH 29/67] make fields nullable --- places/migrations/0002_auto_20230409_1224.py | 23 ++++++++++++++++++++ places/models.py | 8 +++++-- 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 places/migrations/0002_auto_20230409_1224.py diff --git a/places/migrations/0002_auto_20230409_1224.py b/places/migrations/0002_auto_20230409_1224.py new file mode 100644 index 000000000..9a3221b36 --- /dev/null +++ b/places/migrations/0002_auto_20230409_1224.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.15 on 2023-04-09 12:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('places', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='location', + name='latitude', + field=models.FloatField(blank=True, null=True, verbose_name='широта'), + ), + migrations.AlterField( + model_name='location', + name='longitude', + field=models.FloatField(blank=True, null=True, verbose_name='долгота'), + ), + ] diff --git a/places/models.py b/places/models.py index d42fc8429..66ebcb9b5 100644 --- a/places/models.py +++ b/places/models.py @@ -11,10 +11,14 @@ class Location(models.Model): unique=True, ) latitude = models.FloatField( - 'широта' + 'широта', + blank=True, + null=True, ) longitude = models.FloatField( - 'долгота' + 'долгота', + blank=True, + null=True, ) updated_at = models.DateTimeField( 'обновлено', From 2ee90942675524c005eaacbdfe16f270178b0026 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 15:27:20 +0300 Subject: [PATCH 30/67] create empty locations on geocoder fail --- places/models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/places/models.py b/places/models.py index 66ebcb9b5..71beed5d0 100644 --- a/places/models.py +++ b/places/models.py @@ -41,4 +41,10 @@ def update_by_address(cls, address): }, ) except (geopy.exc.GeocoderServiceError, AttributeError): - pass + cls.objects.get_or_create( + address=address, + defaults={ + 'latitude': None, + 'longitude': None, + }, + ) From e22e2d86cccd2386d9772d8975a67327e4c69fc5 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 9 Apr 2023 15:28:14 +0300 Subject: [PATCH 31/67] change default value for DEBUG --- star_burger/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/star_burger/settings.py b/star_burger/settings.py index 1f76e80d9..da06a16bb 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -13,7 +13,7 @@ SECRET_KEY = env('SECRET_KEY') -DEBUG = env.bool('DEBUG', True) +DEBUG = env.bool('DEBUG', False) ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', ['127.0.0.1', 'localhost']) From f361cfd896789c3a99aefe5e1ed7e05c3eec0948 Mon Sep 17 00:00:00 2001 From: mavel Date: Mon, 10 Apr 2023 17:39:55 +0300 Subject: [PATCH 32/67] move code into querysets --- foodcartapp/models.py | 21 +++++++++++++++++++++ restaurateur/views.py | 23 ++++++++++------------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/foodcartapp/models.py b/foodcartapp/models.py index fffea8e6d..fc14f9b56 100644 --- a/foodcartapp/models.py +++ b/foodcartapp/models.py @@ -97,6 +97,15 @@ def __str__(self): return self.name +class RestaurantMenuItemQuerySet(models.QuerySet): + def include_products(self, products: list | models.QuerySet): + return ( + self + .filter(product__in=[product.id for product in products], availability=True) + .prefetch_related('restaurant', 'product') + ) + + class RestaurantMenuItem(models.Model): restaurant = models.ForeignKey( Restaurant, @@ -116,6 +125,8 @@ class RestaurantMenuItem(models.Model): db_index=True ) + objects = RestaurantMenuItemQuerySet.as_manager() + class Meta: verbose_name = 'пункт меню ресторана' verbose_name_plural = 'пункты меню ресторана' @@ -127,6 +138,11 @@ def __str__(self): return f"{self.restaurant.name} - {self.product.name}" +class ProductOrderQuerySet(models.QuerySet): + def in_orders(self, orders: models.QuerySet): + return self.filter(order__in=orders).prefetch_related('product', 'order') + + class ProductOrder(models.Model): product = models.ForeignKey( 'Product', @@ -153,6 +169,8 @@ class ProductOrder(models.Model): blank=True, ) + objects = ProductOrderQuerySet.as_manager() + def __str__(self): return f'{self.product}, {self.quantity}' @@ -161,6 +179,9 @@ class OrderQuerySet(models.QuerySet): def with_totals(self): return self.annotate(total=Sum(F('products_ordered__product_price') * F('products_ordered__quantity'))) + def active(self): + return self.exclude(status=Order.Status.COMPLETE).prefetch_related('assigned_restaurant') + class Order(models.Model): diff --git a/restaurateur/views.py b/restaurateur/views.py index e111e246a..68a24ed60 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -94,28 +94,25 @@ def view_restaurants(request): @user_passes_test(is_manager, login_url='restaurateur:login') def view_orders(request): - orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals().prefetch_related('assigned_restaurant') - ordered_products = ProductOrder.objects.filter(order__in=orders).prefetch_related('product', 'order') - products_in_restaurants = ( - RestaurantMenuItem.objects - .filter(product__in=[order.product for order in ordered_products], availability=True) - .prefetch_related('restaurant', 'product') - ) + active_orders = Order.objects.active().with_totals() + active_product_orders = ProductOrder.objects.in_orders(active_orders) + ordered_products = set(product_order.product for product_order in active_product_orders) + ordered_menu_items = RestaurantMenuItem.objects.include_products(ordered_products) - addresses = [order.address for order in orders] + [pr.restaurant.address for pr in products_in_restaurants] + addresses = [order.address for order in active_orders] + [pr.restaurant.address for pr in ordered_menu_items] locations = Location.objects.filter(address__in=addresses) locations_by_address = {address: None for address in addresses} for location in locations: locations_by_address[location.address] = location - for pr in products_in_restaurants: + for pr in ordered_menu_items: pr.restaurant.location = locations_by_address.get(pr.restaurant.address) - for order in orders: + for order in active_orders: order.location = locations_by_address.get(order.address) - required_product_ids = [op.product.id for op in ordered_products if op.order == order] + required_product_ids = [op.product.id for op in active_product_orders if op.order == order] order.available_restaurants = { - copy(pr.restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids + copy(pr.restaurant) for pr in ordered_menu_items if pr.product.id in required_product_ids } for restaurant in order.available_restaurants: try: @@ -131,5 +128,5 @@ def view_orders(request): ) return render(request, template_name='order_items.html', context={ - 'orders': orders, + 'orders': active_orders, }) From 4047620173805476d243b0067046814bb4dad989 Mon Sep 17 00:00:00 2001 From: mavel Date: Mon, 10 Apr 2023 17:57:21 +0300 Subject: [PATCH 33/67] clean up --- restaurateur/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/restaurateur/views.py b/restaurateur/views.py index 68a24ed60..142ba4c0b 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -105,14 +105,15 @@ def view_orders(request): for location in locations: locations_by_address[location.address] = location - for pr in ordered_menu_items: - pr.restaurant.location = locations_by_address.get(pr.restaurant.address) + for menu_item in ordered_menu_items: + menu_item.restaurant.location = locations_by_address.get(menu_item.restaurant.address) for order in active_orders: order.location = locations_by_address.get(order.address) - required_product_ids = [op.product.id for op in active_product_orders if op.order == order] + required_product_ids = [po.product.id for po in active_product_orders if po.order == order] order.available_restaurants = { - copy(pr.restaurant) for pr in ordered_menu_items if pr.product.id in required_product_ids + copy(menu_item.restaurant) for menu_item in ordered_menu_items + if menu_item.product.id in required_product_ids } for restaurant in order.available_restaurants: try: From 54f28911e345ee9275e0fac092db371d264f6b8f Mon Sep 17 00:00:00 2001 From: mavel Date: Mon, 10 Apr 2023 18:16:37 +0300 Subject: [PATCH 34/67] remove some code --- restaurateur/views.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/restaurateur/views.py b/restaurateur/views.py index 142ba4c0b..2b0e367ea 100644 --- a/restaurateur/views.py +++ b/restaurateur/views.py @@ -101,9 +101,7 @@ def view_orders(request): addresses = [order.address for order in active_orders] + [pr.restaurant.address for pr in ordered_menu_items] locations = Location.objects.filter(address__in=addresses) - locations_by_address = {address: None for address in addresses} - for location in locations: - locations_by_address[location.address] = location + locations_by_address = {location.address: location for location in locations} for menu_item in ordered_menu_items: menu_item.restaurant.location = locations_by_address.get(menu_item.restaurant.address) From e400a72d26c878f5361622d5cd9048bec98abf71 Mon Sep 17 00:00:00 2001 From: mavel Date: Tue, 9 May 2023 02:38:10 +0300 Subject: [PATCH 35/67] add rollbar --- requirements.txt | 1 + star_burger/settings.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/requirements.txt b/requirements.txt index b5cf774ad..24b698f04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ wheel==0.38.4 django-phonenumber-field[phonenumbers]==7.0.2 djangorestframework==3.14.0 geopy~=2.3.0 +rollbar~=0.16.3 diff --git a/star_burger/settings.py b/star_burger/settings.py index da06a16bb..3d73ade78 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -42,6 +42,7 @@ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'debug_toolbar.middleware.DebugToolbarMiddleware', + 'rollbar.contrib.django.middleware.RollbarNotifierMiddleware', ] ROOT_URLCONF = 'star_burger.urls' @@ -130,3 +131,9 @@ PHONENUMBER_DEFAULT_REGION = 'RU' YANDEX_GEO_KEY = env.str('YANDEX_GEO_KEY') + +ROLLBAR = { + 'access_token': env.str('ROLLBAR_TOKEN'), + 'environment': env.str('ROLLBAR_ENV'), + 'root': BASE_DIR, +} From 3be63b8d6eac5f28be80e15adbe8ae7878f9a5f7 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 11 May 2023 01:36:44 +0300 Subject: [PATCH 36/67] use postgres --- README.md | 30 ++++++++++++++++++++++++++++-- requirements.txt | 1 + star_burger/settings.py | 7 ++++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 60d4e5059..dfde50413 100644 --- a/README.md +++ b/README.md @@ -140,17 +140,43 @@ Parcel будет следить за файлами в каталоге `bundle ## Как запустить prod-версию сайта -Собрать фронтенд: +### Собрать фронтенд: ```sh ./node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./" ``` -Настроить бэкенд: создать файл `.env` в каталоге `star_burger/` со следующими настройками: +### Настроить бэкенд: + +Установить зависимости для [psycopg2](https://stackoverflow.com/questions/5420789/how-to-install-psycopg2-with-pip-on-python/30995812#30995812): +```sh +pip install psycopg2 +``` + +Запустить Docker-контейнер с БД (замените все `...` на свои значения): +```sh +docker run -d \ +--name starburger-postgres \ +-p 5432:5432 \ +-e POSTGRES_USER=... \ +-e POSTGRES_PASSWORD=... \ +-e POSTGRES_DB=... \ +-v starburger-postgres:/var/lib/postgresql/data \ +postgres:14 +``` + +Создать файл `.env` в каталоге `star_burger/` со следующими настройками: - `DEBUG` — дебаг-режим. Поставьте `False`. - `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте. - `ALLOWED_HOSTS` — [см. документацию Django](https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts) +- `DB_URL=postgresql://юзер:пароль@localhost:5432/имя-бд` + +Создать таблицы и импортировать данные в БД: +```sh +./manage.py migrate +./manage.py loaddata data/db_dump.json +``` ## Цели проекта diff --git a/requirements.txt b/requirements.txt index 24b698f04..a72d46a29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ django-phonenumber-field[phonenumbers]==7.0.2 djangorestframework==3.14.0 geopy~=2.3.0 rollbar~=0.16.3 +psycopg2~=2.9.6 diff --git a/star_burger/settings.py b/star_burger/settings.py index 3d73ade78..3f8a7e827 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -86,8 +86,9 @@ MEDIA_URL = '/media/' DATABASES = { - 'default': dj_database_url.config( - default='sqlite:////{0}'.format(os.path.join(BASE_DIR, 'db.sqlite3')) + 'default': env.dj_db_url( + 'DB_URL', + default=f'sqlite:////{os.path.join(BASE_DIR, "db.sqlite3")}' ) } @@ -134,6 +135,6 @@ ROLLBAR = { 'access_token': env.str('ROLLBAR_TOKEN'), - 'environment': env.str('ROLLBAR_ENV'), + 'environment': env.str('ROLLBAR_ENV', 'development'), 'root': BASE_DIR, } From 651ebac6fbbf4d39159ca985c245368c8b99ae9e Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 11 May 2023 01:45:35 +0300 Subject: [PATCH 37/67] fix errors in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dfde50413..68c4c3d56 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ Parcel будет следить за файлами в каталоге `bundle Установить зависимости для [psycopg2](https://stackoverflow.com/questions/5420789/how-to-install-psycopg2-with-pip-on-python/30995812#30995812): ```sh -pip install psycopg2 +apt-get install libpq-dev ``` Запустить Docker-контейнер с БД (замените все `...` на свои значения): @@ -161,7 +161,7 @@ docker run -d \ -e POSTGRES_USER=... \ -e POSTGRES_PASSWORD=... \ -e POSTGRES_DB=... \ --v starburger-postgres:/var/lib/postgresql/data \ +-v starburger_postgres:/var/lib/postgresql/data \ postgres:14 ``` From 8e1c710ad9068462e13e5a36adbef040311bd9c6 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 11 May 2023 19:15:11 +0300 Subject: [PATCH 38/67] add a deploy script --- README.md | 29 ++++++++--------------------- scripts/deploy_postgres.sh | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 21 deletions(-) create mode 100755 scripts/deploy_postgres.sh diff --git a/README.md b/README.md index 68c4c3d56..2a8a541c8 100644 --- a/README.md +++ b/README.md @@ -148,34 +148,21 @@ Parcel будет следить за файлами в каталоге `bundle ### Настроить бэкенд: -Установить зависимости для [psycopg2](https://stackoverflow.com/questions/5420789/how-to-install-psycopg2-with-pip-on-python/30995812#30995812): -```sh -apt-get install libpq-dev -``` - -Запустить Docker-контейнер с БД (замените все `...` на свои значения): -```sh -docker run -d \ ---name starburger-postgres \ --p 5432:5432 \ --e POSTGRES_USER=... \ --e POSTGRES_PASSWORD=... \ --e POSTGRES_DB=... \ --v starburger_postgres:/var/lib/postgresql/data \ -postgres:14 -``` - Создать файл `.env` в каталоге `star_burger/` со следующими настройками: - `DEBUG` — дебаг-режим. Поставьте `False`. - `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте. - `ALLOWED_HOSTS` — [см. документацию Django](https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts) -- `DB_URL=postgresql://юзер:пароль@localhost:5432/имя-бд` +- `POSTGRES_USER=...` +- `POSTGRES_PASSWORD=...` +- `POSTGRES_DB=...` +- `DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}` — в этой строке ничего менять не нужно -Создать таблицы и импортировать данные в БД: +Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД. +Затем запустить из каталога `star-burger/` скрипт, который установит зависимости для Postgres и подготовит контейнер с БД: ```sh -./manage.py migrate -./manage.py loaddata data/db_dump.json +chmod u+x scripts/deploy_postgres.sh +scripts/deploy_postgres.sh ``` ## Цели проекта diff --git a/scripts/deploy_postgres.sh b/scripts/deploy_postgres.sh new file mode 100755 index 000000000..77481e978 --- /dev/null +++ b/scripts/deploy_postgres.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ "$EUID" -ne 0 ] + then + echo "Please run as root." + exit +fi + +if [ $( docker ps -a | grep starburger-postgres | wc -l ) -gt 0 ] + then + echo "Postgres container already exists." + exit +fi + +apt-get install libpq-dev + +docker run -d --name starburger-postgres \ +--env-file .env -p 5432:5432 \ +-v starburger_postgres:/var/lib/postgresql/data \ +postgres:14 +sleep 1 +venv/bin/python ./manage.py migrate +venv/bin/python ./manage.py loaddata data/db_dump.json +docker stop -t 10 starburger-postgres From fb752fa58ed25ade2a5e827e0c1cfc76bbb22b08 Mon Sep 17 00:00:00 2001 From: mavel Date: Sat, 13 May 2023 02:01:04 +0300 Subject: [PATCH 39/67] update deploy script --- .gitignore | 1 + README.md | 8 +- package-lock.json | 3224 ++++++++++++++++++++++++++++++++- scripts/deploy_star_burger.sh | 21 + 4 files changed, 3209 insertions(+), 45 deletions(-) create mode 100755 scripts/deploy_star_burger.sh diff --git a/.gitignore b/.gitignore index 9447fd3c4..d6c5988cc 100644 --- a/.gitignore +++ b/.gitignore @@ -143,3 +143,4 @@ cython_debug/ .parcel-cache/ /data/ +staticfiles/ diff --git a/README.md b/README.md index 2a8a541c8..0e29d84fa 100644 --- a/README.md +++ b/README.md @@ -159,12 +159,18 @@ Parcel будет следить за файлами в каталоге `bundle - `DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}` — в этой строке ничего менять не нужно Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД. -Затем запустить из каталога `star-burger/` скрипт, который установит зависимости для Postgres и подготовит контейнер с БД: +Убедиться, что на сервере установлен Docker. +Затем запустить из каталога `star-burger/` скрипт, который установит зависимости и подготовит контейнер с БД: ```sh chmod u+x scripts/deploy_postgres.sh scripts/deploy_postgres.sh ``` +### Подтянуть изменения в репозитории и перезапустить сервисы: +```shell +scripts/deploy_star_burger.sh +``` + ## Цели проекта Код написан в учебных целях — это урок в курсе по Python и веб-разработке на сайте [Devman](https://dvmn.org). За основу был взят код проекта [FoodCart](https://github.com/Saibharath79/FoodCart). diff --git a/package-lock.json b/package-lock.json index b94c7285d..82ac26c33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,3138 @@ { + "name": "star-burger", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, + "packages": { + "": { + "name": "star-burger", + "dependencies": { + "16": "0.0.2", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/preset-react": "^7.16.7", + "core-js": "^3.21.1", + "lodash": "^4.17.21", + "parcel": "^2.4.0", + "react": "^17.0.2", + "react-bootstrap": "^0.33.1", + "react-dom": "^17.0.2", + "react-transition-group": "^4.4.2" + }, + "devDependencies": { + "@babel/core": "^7.17.8" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", + "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", + "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz", + "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz", + "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.16.7", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", + "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz", + "integrity": "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz", + "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-transform-react-display-name": "^7.16.7", + "@babel/plugin-transform-react-jsx": "^7.16.7", + "@babel/plugin-transform-react-jsx-development": "^7.16.7", + "@babel/plugin-transform-react-pure-annotations": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz", + "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs2": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.17.8.tgz", + "integrity": "sha512-KWN7KTjojEVk+hhT7EtvWtSBTueqnPiCT2xPoDFF+ept2Sx9UKnLY7hGsnrNsdx7jvMUQnHoDS6AHCys7i15LA==", + "dependencies": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs2/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@parcel/bundler-default": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.4.0.tgz", + "integrity": "sha512-RaXlxo0M51739Ko3bsOJpDBZlJ+cqkDoBTozNeSc65jS2TMBIBWLMapm8095qmty39OrgYNhzjgPiIlKDS/LWA==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/hash": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.4.0.tgz", + "integrity": "sha512-oOudoAafrCAHQY0zkU7gVHG1pAGBUz9rht7Tx4WupTmAH0O0F5UnZs6XbjoBJaPHg+CYUXK7v9wQcrNA72E3GA==", + "dependencies": { + "@parcel/fs": "2.4.0", + "@parcel/logger": "2.4.0", + "@parcel/utils": "2.4.0", + "lmdb": "2.2.4" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.4.0" + } + }, + "node_modules/@parcel/codeframe": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.4.0.tgz", + "integrity": "sha512-PJ3W9Z0sjoS2CANyo50c+LEr9IRZrtu0WsVPSYZ5ZYRuSXrSa/6PcAlnkyDk2+hi7Od8ncT2bmDexl0Oar3Jyg==", + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/compressor-raw": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.4.0.tgz", + "integrity": "sha512-ZErX14fTc0gKIgtnuqW7Clfln4dpXWfUaJQQIf5C3x/LkpUeEhdXeKntkvSxOddDk2JpIKDwqzAxEMZUnDo4Nw==", + "dependencies": { + "@parcel/plugin": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/config-default": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.4.0.tgz", + "integrity": "sha512-pFOPBXPO6HGqNWTLkcK5i8haMOrRgUouUhcWPGWDpN9IPUYFK2E/O1E/uyMjIA1mSL3FnazI+jJwZ45NhKPpIA==", + "dependencies": { + "@parcel/bundler-default": "2.4.0", + "@parcel/compressor-raw": "2.4.0", + "@parcel/namer-default": "2.4.0", + "@parcel/optimizer-css": "2.4.0", + "@parcel/optimizer-htmlnano": "2.4.0", + "@parcel/optimizer-image": "2.4.0", + "@parcel/optimizer-svgo": "2.4.0", + "@parcel/optimizer-terser": "2.4.0", + "@parcel/packager-css": "2.4.0", + "@parcel/packager-html": "2.4.0", + "@parcel/packager-js": "2.4.0", + "@parcel/packager-raw": "2.4.0", + "@parcel/packager-svg": "2.4.0", + "@parcel/reporter-dev-server": "2.4.0", + "@parcel/resolver-default": "2.4.0", + "@parcel/runtime-browser-hmr": "2.4.0", + "@parcel/runtime-js": "2.4.0", + "@parcel/runtime-react-refresh": "2.4.0", + "@parcel/runtime-service-worker": "2.4.0", + "@parcel/transformer-babel": "2.4.0", + "@parcel/transformer-css": "2.4.0", + "@parcel/transformer-html": "2.4.0", + "@parcel/transformer-image": "2.4.0", + "@parcel/transformer-js": "2.4.0", + "@parcel/transformer-json": "2.4.0", + "@parcel/transformer-postcss": "2.4.0", + "@parcel/transformer-posthtml": "2.4.0", + "@parcel/transformer-raw": "2.4.0", + "@parcel/transformer-react-refresh-wrap": "2.4.0", + "@parcel/transformer-svg": "2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.4.0" + } + }, + "node_modules/@parcel/core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.4.0.tgz", + "integrity": "sha512-EWZ2UWtIuwDc3fgsKyyTLpNNPoG8Yk2L117ICWF/+cqY8z/wJHm2KwLbeplDeq524shav0GJ9O4CemP3JPx0Nw==", + "dependencies": { + "@parcel/cache": "2.4.0", + "@parcel/diagnostic": "2.4.0", + "@parcel/events": "2.4.0", + "@parcel/fs": "2.4.0", + "@parcel/graph": "2.4.0", + "@parcel/hash": "2.4.0", + "@parcel/logger": "2.4.0", + "@parcel/package-manager": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/types": "2.4.0", + "@parcel/utils": "2.4.0", + "@parcel/workers": "2.4.0", + "abortcontroller-polyfill": "^1.1.9", + "base-x": "^3.0.8", + "browserslist": "^4.6.6", + "clone": "^2.1.1", + "dotenv": "^7.0.0", + "dotenv-expand": "^5.1.0", + "json-source-map": "^0.6.1", + "json5": "^2.2.0", + "msgpackr": "^1.5.4", + "nullthrows": "^1.1.1", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css/-/css-1.7.3.tgz", + "integrity": "sha512-rgdRX4Uk31EvzH/mUScL0wdXtkci3U5N1W2pgam+9S10vQy4uONhWBepZ1tUCjONHLacGXr1jp3LbG/HI7LiTw==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/css-darwin-arm64": "1.7.3", + "@parcel/css-darwin-x64": "1.7.3", + "@parcel/css-linux-arm-gnueabihf": "1.7.3", + "@parcel/css-linux-arm64-gnu": "1.7.3", + "@parcel/css-linux-arm64-musl": "1.7.3", + "@parcel/css-linux-x64-gnu": "1.7.3", + "@parcel/css-linux-x64-musl": "1.7.3", + "@parcel/css-win32-x64-msvc": "1.7.3" + } + }, + "node_modules/@parcel/css-darwin-arm64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-darwin-arm64/-/css-darwin-arm64-1.7.3.tgz", + "integrity": "sha512-m3HDY+Rh8HJxmLELKAvCpF59vLS7FWtgBODHxl8G9Jl2CnGtXpXvdpyeMxNsTE+2QuPC+a5QT7IeZAKb2Gjmxg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css-darwin-x64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-darwin-x64/-/css-darwin-x64-1.7.3.tgz", + "integrity": "sha512-LuhweXKxVwrz/hjAOm9XNRMSL+p23px20nhSCASkyUP7Higaxza948W3TSQdoL3YyR+wQxQH8Yj+R/T8Tz3E3g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css-linux-arm-gnueabihf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm-gnueabihf/-/css-linux-arm-gnueabihf-1.7.3.tgz", + "integrity": "sha512-/pd9Em18zMvt7eDZAMpNBEwF7c4VPVhAtBOZ59ClFrsXCTDNYP7mSy0cwNgtLelCRZCGAQmZNBDNQPH7vO3rew==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css-linux-arm64-gnu": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm64-gnu/-/css-linux-arm64-gnu-1.7.3.tgz", + "integrity": "sha512-5aKiEhQK40riO4iVKzRqISzgYK+7Z7i3e6JTSz+/BHuQyHEUaBe/RuJ8Z0BDQtFz0HmWQlrQCd+7hd0Xgd8vYQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css-linux-arm64-musl": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm64-musl/-/css-linux-arm64-musl-1.7.3.tgz", + "integrity": "sha512-Wf7/aIueDED2JqBMfZvzbBAFSaPmd3TR28bD2pmP7CI/jZnm9vHVKMdOLgt9NKSSSjdGrp+VM410CsrUM7xcOw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css-linux-x64-gnu": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-linux-x64-gnu/-/css-linux-x64-gnu-1.7.3.tgz", + "integrity": "sha512-0ZADbuFklUrHC1p2uPY4BPcN07jUTMqJzr/SSdnGN2XiXgiVZGcDCMHUj0DvC9Vwy11DDM6Rnw4QBbKHG+QGjQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css-linux-x64-musl": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-linux-x64-musl/-/css-linux-x64-musl-1.7.3.tgz", + "integrity": "sha512-mFWWM8lX2OIID81YQuDDt9zTqof0B7UcEcs0huE7Zbs60uLEEQupdf8iH0yh5EOhxPt3sRcQnGXf2QTrXdjIMA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/css-win32-x64-msvc": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@parcel/css-win32-x64-msvc/-/css-win32-x64-msvc-1.7.3.tgz", + "integrity": "sha512-KUFEMQcoP7DG3QbsN21OxhjHkfQ1BARn7D9puX75bV5N1F1kv557aaLkQZiMsgiYOL4tmJvsdQXutG7x++3j4Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/diagnostic": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.4.0.tgz", + "integrity": "sha512-TjWO/b2zMFhub5ouwGjazMm7iAUvdmXBfWmjrg4TBhUbhoQwBnyWfvMDtAYo7PcvXfxVPgPZv86Nv6Ym5H6cHQ==", + "dependencies": { + "json-source-map": "^0.6.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/events": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.4.0.tgz", + "integrity": "sha512-DEaEtFbhOhNAEmiXJ3MyF8Scq+sNDKiTyLax4lAC5/dpE5GvwfNnoD17C2+0gDuuDpdQkdHfXfvr50aYFt7jcw==", + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/fs": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.4.0.tgz", + "integrity": "sha512-CnUlWGUJ52SJVQi8QnaAPPQZOADmHMV9D9aX9GLcDm5XLT3Em7vmesG4bNLdMLwzYuzAtenhcWmuRCACuYztHw==", + "dependencies": { + "@parcel/fs-search": "2.4.0", + "@parcel/types": "2.4.0", + "@parcel/utils": "2.4.0", + "@parcel/watcher": "^2.0.0", + "@parcel/workers": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.4.0" + } + }, + "node_modules/@parcel/fs-search": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.4.0.tgz", + "integrity": "sha512-W/Vu6wbZk4wuB6AVdMkyymwh/S8Peed/PgJgSsApYD6lSTD315I6OuEdxZh3lWY+dqQdog/NJ7dvi/hdpH/Iqw==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/graph": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.4.0.tgz", + "integrity": "sha512-5TZIAfDITkJCzgH4j4OQhnIvjV9IFwWqNBJanRl5QQTmKvdcODS3WbnK1SOJ+ZltcLVXMB+HNXmL0bX0tVolcw==", + "dependencies": { + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/hash": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.4.0.tgz", + "integrity": "sha512-nB+wYNUhe6+G8M7vQhdeFXtpYJYwJgBHOPZ7Hd9O2jdlamWjDbw0t/u1dJbYvGJ8ZDtLDwiItawQVpuVdskQ9g==", + "dependencies": { + "detect-libc": "^1.0.3", + "xxhash-wasm": "^0.4.2" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/logger": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.4.0.tgz", + "integrity": "sha512-DqfU0Zcs/0a7VBk+MsjJ80C66w4kM9EbkO3G12NIyEjNeG50ayW2CE9rUuJ91JaM9j0NFM1P82eyLpQPFFaVPw==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/events": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/markdown-ansi": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.4.0.tgz", + "integrity": "sha512-gPUP1xikxHiu2kFyPy35pfuVkFgAmcywO8YDQj7iYcB+k7l4QPpIYFYGXn2QADV4faf66ncMeTD4uYV8c0GqjQ==", + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/namer-default": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.4.0.tgz", + "integrity": "sha512-DfL+Gx0Tyoa0vsgRpNybXjuKbWNw8MTVpy7Dk7r0btfVsn1jy3SSwlxH4USf76gb00/pK6XBsMp9zn7Z8ePREQ==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/node-resolver-core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.4.0.tgz", + "integrity": "sha512-qiN97XcfW2fYNoYuVEhNKuVPEJKj5ONQl0fqr/NEMmYvWz3bVKjgiXNJwW558elZvCI08gEbdxgyThpuFFQeKQ==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-css": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.4.0.tgz", + "integrity": "sha512-LQmjjOGsHEHKTJqfHR2eJyhWhLXvHP0uOAU+qopBttYYlB2J/vMK9RYAye5cyAb8bQmV8wAdi2mq9rnt7FMSPw==", + "dependencies": { + "@parcel/css": "^1.7.2", + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/utils": "2.4.0", + "browserslist": "^4.6.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-htmlnano": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.4.0.tgz", + "integrity": "sha512-02EbeElLgNOAYhGU7fFBahpoKrX5G/yzahpaoKB/ypScM4roSsAMBkGcluboR5L10YRsvfvJEpxvfGyDA3tPmw==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "htmlnano": "^2.0.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "svgo": "^2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-image": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.4.0.tgz", + "integrity": "sha512-Q4onaBMPkDyYxPzrb8ytBUftaQZFepj9dSUgq+ETuHDzkgia0tomDPfCqrw6ld0qvYyANzXTP5+LC4g0i5yh+A==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "@parcel/workers": "2.4.0", + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-svgo": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.4.0.tgz", + "integrity": "sha512-mwvGuCqVuNCAuMlp2maFE/Uz9ud1T1AuX0f6cCRczjFYiwZuIr/0iDdfFzSziOkVo1MRAGAZNa0dRR/UzCZtVg==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "svgo": "^2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-terser": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.4.0.tgz", + "integrity": "sha512-PdCgRgXNSY6R1HTV9VG2MHp1CgUbP5pslCyxvlbUmQAS6bvEpMOpn3qSd+U28o7mGE/qXIhvpDyi808sb+MEcg==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1", + "terser": "^5.2.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/package-manager": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.4.0.tgz", + "integrity": "sha512-21AEfAQnZbHRVViTn7QsPGe/CiGaFaDUH5f0m8qVC7fDjjhC8LM8blkqU72goaO9FbaLMadtEf2txhzly7h/bg==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/fs": "2.4.0", + "@parcel/logger": "2.4.0", + "@parcel/types": "2.4.0", + "@parcel/utils": "2.4.0", + "@parcel/workers": "2.4.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.4.0" + } + }, + "node_modules/@parcel/packager-css": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.4.0.tgz", + "integrity": "sha512-LmPDWzkXi60Oy3WrPF0jPKQxeTwW5hmNBgrcXJMHSu+VcXdaQZNzNxVzhnZkJUbDd2z9vAUrUGzdLh8TquC8iQ==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-html": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.4.0.tgz", + "integrity": "sha512-OPMIQ1uHYQFpRPrsmm5BqONbAyzjlhVsPRAzHlcBrglG4BTUeOR2ow4MUKblHmVVqc3QHnfZG4nHHtFkeuNQ3A==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/types": "2.4.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.4.0.tgz", + "integrity": "sha512-cfslIH43CJFgBS9PmdFaSnbInMCoejsFCnxtJa2GeUpjCXSfelPRp0OPx7m8n+fap4czftPhoxBALeDUElOZGQ==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/hash": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/utils": "2.4.0", + "globals": "^13.2.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-raw": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.4.0.tgz", + "integrity": "sha512-SFfw7chMFITj3J26ZVDJxbO6xwtPFcFBm1js8cwWMgzwuwS6CEc43k5+Abj+2/EqHU9kNJU9eWV5vT6lQwf3HA==", + "dependencies": { + "@parcel/plugin": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-svg": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.4.0.tgz", + "integrity": "sha512-DwkgrdLEQop+tu9Ocr1ZaadmpsbSgVruJPr80xq1LaB0Jiwrl9HjHStMNH1laNFueK1yydxhnj9C2JQfW28qag==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/types": "2.4.0", + "@parcel/utils": "2.4.0", + "posthtml": "^0.16.4" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.4.0.tgz", + "integrity": "sha512-ehFUAL2+h27Lv+cYbbXA74UGy8C+eglUjcpvASOOjVRFuD6poMAMliKkKAXBhQaFx/Rvhz27A2PIPv9lL2i4UQ==", + "dependencies": { + "@parcel/types": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-cli": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.4.0.tgz", + "integrity": "sha512-Q9bIFMaGvQgypCDxdMEKOwrJzIHAXScKkuFsqTHnUL6mmH3Mo2CoEGAq/wpMXuPhXRn1dPJcHgTNDwZ2fSzz0A==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/types": "2.4.0", + "@parcel/utils": "2.4.0", + "chalk": "^4.1.0", + "term-size": "^2.2.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-dev-server": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.4.0.tgz", + "integrity": "sha512-24h++wevs7XYuX4dKa4PUfLSstvn3g7udajFv6CeQoME+dR25RL/wH/2LUbhV5ilgXXab76rWIndSqp78xHxPA==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/resolver-default": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.4.0.tgz", + "integrity": "sha512-K7pIIFmGm1hjg/7Mzkg99i8tfCClKfBUTuc2R5j8cdr2n0mCAi4/f2mFf5svLrb5XZrnDgoQ05tHKklLEfUDUw==", + "dependencies": { + "@parcel/node-resolver-core": "2.4.0", + "@parcel/plugin": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-browser-hmr": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.4.0.tgz", + "integrity": "sha512-swPFtvxGoCA9LEjU/pHPNjxG1l0fte8447zXwRN/AaYrtjNu9Ww117OSKCyvCnE143E79jZOFStodTQGFuH+9A==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.4.0.tgz", + "integrity": "sha512-67OOvmkDdtmgzZVP/EyAzoXhJ/Ug3LUVUt7idg9arun5rdJptqEb3Um3wmH0zjcNa9jMbJt7Kl5x1wA8dJgPYg==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-react-refresh": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.4.0.tgz", + "integrity": "sha512-flnr+bf06lMZPbXZZLLaFNrPHvYpfuXTVovEghyUW46qLVpaHj33dpsU/LqZplIuHgBp2ibgrKhr/hY9ell68w==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "react-refresh": "^0.9.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-service-worker": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.4.0.tgz", + "integrity": "sha512-RgM5QUqW22WzstW03CtV+Oih8VGVuwsf94Cc4hLouU2EAD0NUJgATWbFocZVTZIBTKELAWh2gjpSQDdnL4Ur+A==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/source-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.0.2.tgz", + "integrity": "sha512-NnUrPYLpYB6qyx2v6bcRPn/gVigmGG6M6xL8wIg/i0dP1GLkuY1nf+Hqdf63FzPTqqT7K3k6eE5yHPQVMO5jcA==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": "^12.18.3 || >=14" + } + }, + "node_modules/@parcel/transformer-babel": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.4.0.tgz", + "integrity": "sha512-iWDa7KzJTMP3HNmrYxiYq/S6redk2qminx/9MwmKIN9jzm8mgts2Lj9lOg/t66YaDGky6JAvw4DhB2qW4ni6yQ==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/utils": "2.4.0", + "browserslist": "^4.6.6", + "json5": "^2.2.0", + "nullthrows": "^1.1.1", + "semver": "^5.7.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-css": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.4.0.tgz", + "integrity": "sha512-D2u48LuiQsQvbknABE0wVKFp9r6yCgWrHKEP1J6EJ31c49nXGXDHrpHJJwqq9BvAs/124eBI5mSsehTJyFEMwg==", + "dependencies": { + "@parcel/css": "^1.7.2", + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/utils": "2.4.0", + "browserslist": "^4.6.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.4.0.tgz", + "integrity": "sha512-2/8X/o5QaCNVPr4wkxLCUub7v/YVvVN2L5yCEcTatNeFhNg/2iz7P2ekfqOaoDCHWZEOBT1VTwPbdBt+TMM71Q==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/hash": "2.4.0", + "@parcel/plugin": "2.4.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html/node_modules/posthtml-parser": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", + "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@parcel/transformer-image": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.4.0.tgz", + "integrity": "sha512-JZkQvGGoGiD0AVKLIbAYYUWxepMmUaWZ4XXx71MmS/kA7cUDwTZ0CXq63YnSY1m+DX+ClTuTN8mBlwe2dkcGbA==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/workers": "2.4.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + } + }, + "node_modules/@parcel/transformer-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.4.0.tgz", + "integrity": "sha512-eeLHFwv3jT3GmIxpLC7B8EXExGK0MFaK91HXljOMh6l8a+GlQYw27MSFQVtoXr0Olx9Uq2uvjXP1+zSsq3LQUQ==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/utils": "2.4.0", + "@parcel/workers": "2.4.0", + "@swc/helpers": "^0.3.6", + "browserslist": "^4.6.6", + "detect-libc": "^1.0.3", + "nullthrows": "^1.1.1", + "regenerator-runtime": "^0.13.7", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-json": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.4.0.tgz", + "integrity": "sha512-3nR+d39mbURoXIypDfVCaxpwL65qMV+h8SLD78up2uhaRGklHQfN7GuemR7L+mcVAgNrmwVvZHhyNjdgYwWqqg==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "json5": "^2.2.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-postcss": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.4.0.tgz", + "integrity": "sha512-ijIa2x+dbKnJhr7zO5WlXkvuj832fDoGksMBk2DX3u2WMrbh2rqVWPpGFsDhESx7EAy38nUoV/5KUdrNqUmCEA==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/hash": "2.4.0", + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "clone": "^2.1.1", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-posthtml": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.4.0.tgz", + "integrity": "sha512-xoL3AzgtVeRRAo6bh0AHAYm9bt1jZ+HiH86/7oARj/uJs6Wd8kXK/DZf6fH+F87hj4e7bnjmDDc0GPVK0lPz1w==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/posthtml-parser": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", + "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@parcel/transformer-raw": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.4.0.tgz", + "integrity": "sha512-fciFbNrzj0kLlDgr6OsI0PUv414rVygDWAsgbCCq4BexDkuemMs9f9FjMctx9B2VZlctE8dTT4RGkuQumTIpUg==", + "dependencies": { + "@parcel/plugin": "2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-react-refresh-wrap": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.4.0.tgz", + "integrity": "sha512-9+f6sGOWkf0jyUQ1CuFWk+04Mq3KTOCU9kRiwCHX1YdUCv5uki6r9XUSpqiYodrV+L6w9CCwLvGMLCDHxtCxMg==", + "dependencies": { + "@parcel/plugin": "2.4.0", + "@parcel/utils": "2.4.0", + "react-refresh": "^0.9.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-svg": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.4.0.tgz", + "integrity": "sha512-D+yzVtSxtQML3d26fd/g4E/xYW68+OMbMUVLXORtoYMU42fnXQkJP6jGOdqy8Td+WORNY7EwVtQnESLwhBmolw==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/hash": "2.4.0", + "@parcel/plugin": "2.4.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/posthtml-parser": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", + "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@parcel/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.4.0.tgz", + "integrity": "sha512-nysGIbBEnp+7R+tKTysdcTFOZDTCodsiXFeAhYQa5bhiOnG1l9gzhxQnE2OsdsgvMm40IOsgKprqvM/DbdLfnQ==", + "dependencies": { + "@parcel/cache": "2.4.0", + "@parcel/diagnostic": "2.4.0", + "@parcel/fs": "2.4.0", + "@parcel/package-manager": "2.4.0", + "@parcel/source-map": "^2.0.0", + "@parcel/workers": "2.4.0", + "utility-types": "^3.10.0" + } + }, + "node_modules/@parcel/utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.4.0.tgz", + "integrity": "sha512-sdNo+mZqDZT8LJYB6WWRKa4wFVZcK6Zb5Jh6Du76QvXXwHbPIQNZgJBb6gd/Rbk4GLOp2tW7MnBfq6zP9E9E2g==", + "dependencies": { + "@parcel/codeframe": "2.4.0", + "@parcel/diagnostic": "2.4.0", + "@parcel/hash": "2.4.0", + "@parcel/logger": "2.4.0", + "@parcel/markdown-ansi": "2.4.0", + "@parcel/source-map": "^2.0.0", + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.5.tgz", + "integrity": "sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/workers": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.4.0.tgz", + "integrity": "sha512-eSFyvEoXXPgFzQfKIlpkUjpHfIbezUCRFTPKyJAKCxvU5DSXOpb1kz5vDESWQ4qTZXKnrKvxS1PPWN6bam9z0g==", + "dependencies": { + "@parcel/diagnostic": "2.4.0", + "@parcel/logger": "2.4.0", + "@parcel/types": "2.4.0", + "@parcel/utils": "2.4.0", + "chrome-trace-event": "^1.0.2", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.4.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.8.tgz", + "integrity": "sha512-aWItSZvJj4+GI6FWkjZR13xPNPctq2RRakzo+O6vN7bC2yjwdg5EFpgaSAUn95b7BGSgcflvzVDPoKmJv24IOg==" + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" + }, + "node_modules/@types/react": { + "version": "17.0.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", + "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/16": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/16/-/16-0.0.2.tgz", + "integrity": "sha512-AhG4lpdn+/it+U5Xl1bm5SbaHYTH5NfU/vXZkP7E7CHjtVtITuFVZKa3AZP3gN38RDJHYYtEqWmqzCutlXaR7w==", + "dependencies": { + "numeric": "^1.2.6" + } + }, + "node_modules/abortcontroller-polyfill": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", + "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==" + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "node_modules/browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001320", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", + "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/core-js": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", + "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csstype": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", + "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.92", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.92.tgz", + "integrity": "sha512-YAVbvQIcDE/IJ/vzDMjD484/hsRbFPW2qXJPaYTfOhtligmfYEYOep+5QojpaEU9kq6bMvNeC2aG7arYvTHYsA==" + }, + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-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=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/htmlnano": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.0.tgz", + "integrity": "sha512-thKQfhcp2xgtsWNE27A2bliEeqVL5xjAgGn0wajyttvFFsvFWWah1ntV9aEX61gz0T6MBQ5xK/1lXuEumhJTcg==", + "dependencies": { + "cosmiconfig": "^7.0.1", + "posthtml": "^0.16.5", + "timsort": "^0.3.0" + }, + "peerDependencies": { + "cssnano": "^5.0.11", + "postcss": "^8.3.11", + "purgecss": "^4.0.3", + "relateurl": "^0.2.7", + "srcset": "^5.0.0", + "svgo": "^2.8.0", + "terser": "^5.10.0", + "uncss": "^0.17.3" + }, + "peerDependenciesMeta": { + "cssnano": { + "optional": true + }, + "postcss": { + "optional": true + }, + "purgecss": { + "optional": true + }, + "relateurl": { + "optional": true + }, + "srcset": { + "optional": true + }, + "svgo": { + "optional": true + }, + "terser": { + "optional": true + }, + "uncss": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha1-a+Fm0USCihMdaGiRuYPfYsOUkf8=" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/json-source-map/-/json-source-map-0.6.1.tgz", + "integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keycode": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", + "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/lmdb": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.2.4.tgz", + "integrity": "sha512-gto+BB2uEob8qRiTlOq+R3uX0YNHsX9mjxj9Sbdue/LIKqu6IlZjrsjKeGyOMquc/474GEqFyX2pdytpydp0rQ==", + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.5.4", + "nan": "^2.14.2", + "node-gyp-build": "^4.2.3", + "ordered-binary": "^1.2.4", + "weak-lru-cache": "^1.2.2" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/msgpackr": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.5.5.tgz", + "integrity": "sha512-JG0V47xRIQ9pyUnx6Hb4+3TrQoia2nA3UIdmyTldhxaxtKFkekkKpUW/N6fwHwod9o4BGuJGtouxOk+yCP5PEA==", + "optionalDependencies": { + "msgpackr-extract": "^1.0.14" + } + }, + "node_modules/msgpackr-extract": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-1.0.16.tgz", + "integrity": "sha512-fxdRfQUxPrL/TizyfYfMn09dK58e+d65bRD/fcaVH4052vj30QOzzqxcQIS7B0NsqlypEQ/6Du3QmP2DhWFfCA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.2", + "node-gyp-build": "^4.2.3" + } + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" + }, + "node_modules/numeric": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz", + "integrity": "sha512-avBiDAP8siMa7AfJgYyuxw1oyII4z2sswS23+O+ZfV28KrtNzy0wxUFwi4f3RyM4eeeXNs1CThxR7pb5QQcMiw==" + }, + "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=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ordered-binary": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.2.4.tgz", + "integrity": "sha512-A/csN0d3n+igxBPfUrjbV5GC69LWj2pjZzAAeeHXLukQ4+fytfP4T1Lg0ju7MSPSwq7KtHkGaiwO8URZN5IpLg==" + }, + "node_modules/parcel": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.4.0.tgz", + "integrity": "sha512-dPWpu4RnxG9HqiLvaF8COEWEnT/KrigrC6PyPaQ0zEgpBfp7/jzXZFBVaZk2N+lpvrbNEYMjN9bv5UQGJJszIw==", + "dependencies": { + "@parcel/config-default": "2.4.0", + "@parcel/core": "2.4.0", + "@parcel/diagnostic": "2.4.0", + "@parcel/events": "2.4.0", + "@parcel/fs": "2.4.0", + "@parcel/logger": "2.4.0", + "@parcel/package-manager": "2.4.0", + "@parcel/reporter-cli": "2.4.0", + "@parcel/reporter-dev-server": "2.4.0", + "@parcel/utils": "2.4.0", + "chalk": "^4.1.0", + "commander": "^7.0.0", + "get-port": "^4.2.0", + "v8-compile-cache": "^2.0.0" + }, + "bin": { + "parcel": "lib/bin.js" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-bootstrap": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.33.1.tgz", + "integrity": "sha512-qWTRravSds87P8WC82tETy2yIso8qDqlIm0czsrduCaYAFtHuyLu0XDbUlfLXeRzqgwm5sRk2wRaTNoiVkk/YQ==", + "dependencies": { + "@babel/runtime-corejs2": "^7.0.0", + "classnames": "^2.2.5", + "dom-helpers": "^3.2.0", + "invariant": "^2.2.4", + "keycode": "^2.2.0", + "prop-types": "^15.6.1", + "prop-types-extra": "^1.0.1", + "react-overlays": "^0.9.0", + "react-prop-types": "^0.4.0", + "react-transition-group": "^2.0.0", + "uncontrollable": "^7.0.2", + "warning": "^3.0.0" + }, + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, + "node_modules/react-bootstrap/node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/react-bootstrap/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, + "node_modules/react-bootstrap/node_modules/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-overlays": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.9.3.tgz", + "integrity": "sha512-u2T7nOLnK+Hrntho4p0Nxh+BsJl0bl4Xuwj/Y0a56xywLMetgAfyjnDVrudLXsNcKGaspoC+t3C1V80W9QQTdQ==", + "dependencies": { + "classnames": "^2.2.5", + "dom-helpers": "^3.2.1", + "prop-types": "^15.5.10", + "prop-types-extra": "^1.0.1", + "react-transition-group": "^2.2.1", + "warning": "^3.0.0" + }, + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, + "node_modules/react-overlays/node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/react-overlays/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, + "node_modules/react-overlays/node_modules/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/react-prop-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", + "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=", + "dependencies": { + "warning": "^3.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/react-prop-types/node_modules/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.3.tgz", + "integrity": "sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==", + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, + "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=", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==" + }, + "node_modules/xxhash-wasm": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", + "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + } + }, "dependencies": { "16": { "version": "0.0.2", @@ -14,7 +3146,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.0" } @@ -30,14 +3161,12 @@ "@babel/compat-data": { "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "dev": true + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==" }, "@babel/core": { "version": "7.17.8", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", - "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -59,8 +3188,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -93,7 +3221,6 @@ "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dev": true, "requires": { "@babel/compat-data": "^7.17.7", "@babel/helper-validator-option": "^7.16.7", @@ -104,8 +3231,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -177,7 +3303,6 @@ "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -218,7 +3343,6 @@ "version": "7.17.7", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, "requires": { "@babel/types": "^7.17.0" } @@ -245,7 +3369,6 @@ "version": "7.17.8", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", - "dev": true, "requires": { "@babel/template": "^7.16.7", "@babel/traverse": "^7.17.3", @@ -447,26 +3570,47 @@ "to-fast-properties": "^2.0.0" } }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "@parcel/bundler-default": { @@ -1308,7 +4452,6 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" }, @@ -1316,8 +4459,7 @@ "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 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -1480,8 +4622,7 @@ "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 + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, "get-port": { "version": "4.2.0", @@ -1570,9 +4711,9 @@ "integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==" }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "keycode": { "version": "2.2.1", @@ -2013,13 +5154,13 @@ "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==" }, "terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.3.tgz", + "integrity": "sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==", "requires": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "dependencies": { @@ -2027,11 +5168,6 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" } } }, diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh new file mode 100755 index 000000000..0565621cf --- /dev/null +++ b/scripts/deploy_star_burger.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e + +if [ "$EUID" -ne 0 ] + then + echo "Please run as root." + exit +fi + +git pull +npm ci --dev +npm audit fix +venv/bin/pip install -U -r requirements.txt +node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./" +venv/bin/python manage.py collectstatic --noinput +venv/bin/python manage.py migrate +systemctl restart starburger-django.service +systemctl reload nginx + +echo "Project updated." From 4518911a20d525054283e059c7d69bfadf212dd6 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 14 May 2023 11:36:35 +0300 Subject: [PATCH 40/67] notify rollbar of deploys --- README.md | 4 +-- scripts/deploy_star_burger.sh | 2 ++ .../{deploy_postgres.sh => init_postgres.sh} | 0 scripts/report_deploy_rollbar.py | 26 +++++++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) rename scripts/{deploy_postgres.sh => init_postgres.sh} (100%) create mode 100644 scripts/report_deploy_rollbar.py diff --git a/README.md b/README.md index 0e29d84fa..5507b5f4d 100644 --- a/README.md +++ b/README.md @@ -162,8 +162,8 @@ Parcel будет следить за файлами в каталоге `bundle Убедиться, что на сервере установлен Docker. Затем запустить из каталога `star-burger/` скрипт, который установит зависимости и подготовит контейнер с БД: ```sh -chmod u+x scripts/deploy_postgres.sh -scripts/deploy_postgres.sh +chmod u+x scripts/init_postgres.sh +scripts/init_postgres.sh ``` ### Подтянуть изменения в репозитории и перезапустить сервисы: diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh index 0565621cf..dd2ae9108 100755 --- a/scripts/deploy_star_burger.sh +++ b/scripts/deploy_star_burger.sh @@ -18,4 +18,6 @@ venv/bin/python manage.py migrate systemctl restart starburger-django.service systemctl reload nginx +venv/bin/python scripts/report_deploy_rollbar.py + echo "Project updated." diff --git a/scripts/deploy_postgres.sh b/scripts/init_postgres.sh similarity index 100% rename from scripts/deploy_postgres.sh rename to scripts/init_postgres.sh diff --git a/scripts/report_deploy_rollbar.py b/scripts/report_deploy_rollbar.py new file mode 100644 index 000000000..d021cff23 --- /dev/null +++ b/scripts/report_deploy_rollbar.py @@ -0,0 +1,26 @@ +import subprocess + +import requests +from environs import Env + + +env = Env() +env.read_env() + +commit_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], encoding='UTF-8').strip() + +url = 'https://api.rollbar.com/api/1/deploy' +headers = { + 'accept': 'application/json', + 'content-type': 'application/json', + 'X-Rollbar-Access-Token': env.str('ROLLBAR_TOKEN'), +} +data = { + 'environment': env.str('ROLLBAR_ENV'), + 'revision': commit_hash, + # 'status': 'succeeded', + 'rollbar_username': env.str('ROLLBAR_USERNAME') +} + +response = requests.post(url, headers=headers, json=data) +response.raise_for_status() From d72fb6e480509d89e842d8379bbfb8d58407394f Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 14 May 2023 11:43:47 +0300 Subject: [PATCH 41/67] use a full commit hash --- scripts/report_deploy_rollbar.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/report_deploy_rollbar.py b/scripts/report_deploy_rollbar.py index d021cff23..7e622cca7 100644 --- a/scripts/report_deploy_rollbar.py +++ b/scripts/report_deploy_rollbar.py @@ -1,3 +1,5 @@ +"""Helper script to be used at the end of deploy_star_burger.sh""" + import subprocess import requests @@ -7,7 +9,7 @@ env = Env() env.read_env() -commit_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], encoding='UTF-8').strip() +commit_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'], encoding='UTF-8').strip() url = 'https://api.rollbar.com/api/1/deploy' headers = { @@ -15,12 +17,11 @@ 'content-type': 'application/json', 'X-Rollbar-Access-Token': env.str('ROLLBAR_TOKEN'), } -data = { +payload = { 'environment': env.str('ROLLBAR_ENV'), 'revision': commit_hash, - # 'status': 'succeeded', 'rollbar_username': env.str('ROLLBAR_USERNAME') } -response = requests.post(url, headers=headers, json=data) +response = requests.post(url, headers=headers, json=payload) response.raise_for_status() From cdf536c62728af4ac2f42b99dccb01dfc9274735 Mon Sep 17 00:00:00 2001 From: mavel-x <96616212+mavel-x@users.noreply.github.com> Date: Sun, 14 May 2023 12:17:16 +0300 Subject: [PATCH 42/67] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5507b5f4d..0c70d3e58 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Сайт доставки еды Star Burger +# Сайт доставки еды [Star Burger](https://starburger.mavel.cc/) Это сайт сети ресторанов Star Burger. Здесь можно заказать превосходные бургеры с доставкой на дом. From 99f8c559a11f1c63911907b1746db9f0be7dc8be Mon Sep 17 00:00:00 2001 From: mavel Date: Tue, 16 May 2023 01:19:32 +0300 Subject: [PATCH 43/67] unrequire Rollbar, update README --- README.md | 6 ++++++ star_burger/settings.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c70d3e58..77f727166 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,12 @@ chmod u+x scripts/init_postgres.sh scripts/init_postgres.sh ``` +Чтобы получать мгновенные уведомления об ошибках, подключите свой аккаунт [Rollbar](https://docs.rollbar.com/docs/setup) +и добавьте следующие переменные в `.env`: +- `ROLLBAR_TOKEN=...` +- `ROLLBAR_ENV=...` - development/production/... +- `ROLLBAR_USERNAME=...` + ### Подтянуть изменения в репозитории и перезапустить сервисы: ```shell scripts/deploy_star_burger.sh diff --git a/star_burger/settings.py b/star_burger/settings.py index 3f8a7e827..f50f2a1fb 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -134,7 +134,7 @@ YANDEX_GEO_KEY = env.str('YANDEX_GEO_KEY') ROLLBAR = { - 'access_token': env.str('ROLLBAR_TOKEN'), + 'access_token': env.str('ROLLBAR_TOKEN', None), 'environment': env.str('ROLLBAR_ENV', 'development'), 'root': BASE_DIR, } From 64f0fbf58c5a7e222e3ec5ae0d63bc4ea9dc3577 Mon Sep 17 00:00:00 2001 From: mavel Date: Wed, 17 May 2023 14:20:22 +0300 Subject: [PATCH 44/67] add no-input to script --- scripts/deploy_star_burger.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh index dd2ae9108..bab2c0b81 100755 --- a/scripts/deploy_star_burger.sh +++ b/scripts/deploy_star_burger.sh @@ -14,7 +14,7 @@ npm audit fix venv/bin/pip install -U -r requirements.txt node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./" venv/bin/python manage.py collectstatic --noinput -venv/bin/python manage.py migrate +venv/bin/python manage.py migrate --noinput systemctl restart starburger-django.service systemctl reload nginx From 76eee59435f2a8cffc56eb42ad4606e5c979819a Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Jul 2023 16:21:30 +0200 Subject: [PATCH 45/67] prepare for docker compose deploy --- .dockerignore | 3 +++ .gitignore | 1 - Dockerfile | 20 +++++++++++++++ README.md | 29 ++++++++++------------ bundles/.gitkeep | 0 docker-compose.yml | 42 ++++++++++++++++++++++++++++++++ nginx.conf | 21 ++++++++++++++++ requirements.txt | 1 + scripts/deploy_star_burger.sh | 23 ----------------- scripts/first_deploy.sh | 11 +++++++++ scripts/init_postgres.sh | 24 ------------------ scripts/report_deploy_rollbar.py | 2 -- star_burger/settings.py | 22 ++++++++++++----- starburger.service.template | 15 ++++++++++++ 14 files changed, 142 insertions(+), 72 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100644 bundles/.gitkeep create mode 100644 docker-compose.yml create mode 100644 nginx.conf delete mode 100755 scripts/deploy_star_burger.sh create mode 100755 scripts/first_deploy.sh delete mode 100755 scripts/init_postgres.sh create mode 100644 starburger.service.template diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..45acdd4a4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +node_modules +venv +staticfiles diff --git a/.gitignore b/.gitignore index d6c5988cc..28abf0a39 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -bundles/** node_modules/ media/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..38df5dbfa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM node:16.20 AS parcel +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY bundles-src/ ./bundles-src/ +RUN ./node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./" + +FROM python:3.10 +WORKDIR /app +COPY --from=parcel /app/bundles/ ./bundles/ +RUN apt update \ + && apt install -y libpq-dev \ + && rm -rf /var/lib/apt/lists/* +COPY requirements.txt ./ +RUN pip install -r requirements.txt +COPY . . +RUN python manage.py collectstatic --noinput +RUN mkdir -p frontend/ \ + && cp -R bundles/* frontend/ \ + && cp -R staticfiles/* frontend/ diff --git a/README.md b/README.md index 77f727166..e3bb4e45b 100644 --- a/README.md +++ b/README.md @@ -140,15 +140,9 @@ Parcel будет следить за файлами в каталоге `bundle ## Как запустить prod-версию сайта -### Собрать фронтенд: - -```sh -./node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./" -``` - ### Настроить бэкенд: -Создать файл `.env` в каталоге `star_burger/` со следующими настройками: +Создать файл `.env` в корневом каталоге проекта со следующими настройками: - `DEBUG` — дебаг-режим. Поставьте `False`. - `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте. @@ -156,15 +150,9 @@ Parcel будет следить за файлами в каталоге `bundle - `POSTGRES_USER=...` - `POSTGRES_PASSWORD=...` - `POSTGRES_DB=...` -- `DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}` — в этой строке ничего менять не нужно Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД. Убедиться, что на сервере установлен Docker. -Затем запустить из каталога `star-burger/` скрипт, который установит зависимости и подготовит контейнер с БД: -```sh -chmod u+x scripts/init_postgres.sh -scripts/init_postgres.sh -``` Чтобы получать мгновенные уведомления об ошибках, подключите свой аккаунт [Rollbar](https://docs.rollbar.com/docs/setup) и добавьте следующие переменные в `.env`: @@ -172,11 +160,20 @@ scripts/init_postgres.sh - `ROLLBAR_ENV=...` - development/production/... - `ROLLBAR_USERNAME=...` -### Подтянуть изменения в репозитории и перезапустить сервисы: -```shell -scripts/deploy_star_burger.sh +### Поднять контейнеры и запустить приложение: +```sh +scripts/first_deploy.sh ``` +Приложение контролируется systemd и запускается автоматически при перезагрузке сервера. +Команды, которые могут пригодиться: +```sh +systemctl stop starburger # остановить +systemctl start starburger # запустить +systemctl status starburger # посмотреть на статус и stdout +``` + + ## Цели проекта Код написан в учебных целях — это урок в курсе по Python и веб-разработке на сайте [Devman](https://dvmn.org). За основу был взят код проекта [FoodCart](https://github.com/Saibharath79/FoodCart). diff --git a/bundles/.gitkeep b/bundles/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..f4300fcc7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +version: '3' + +services: + db: + image: postgres:14 + environment: + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + volumes: + - postgres:/var/lib/postgresql/data + + django: + build: . + command: gunicorn -b 0.0.0.0:8081 --workers 3 star_burger.wsgi:application + environment: + - POSTGRES_HOST=db + - ALLOWED_HOSTS=127.0.0.1,starburger.mavel.cc + volumes: + - .:/app + - frontend:/app/frontend + - media:/app/media + ports: + - "8081:8081" + depends_on: + - db + + nginx: + image: nginx:1.25 + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf + - frontend:/frontend + - media:/media + depends_on: + - django + +volumes: + postgres: + frontend: + media: diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 000000000..45ea4ae4f --- /dev/null +++ b/nginx.conf @@ -0,0 +1,21 @@ +server { + server_name starburger.mavel.cc www.starburger.mavel.cc; + listen 0:80; + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://django:8081/; + } + + location /static/ { + alias '/frontend/'; + } + + location /media/ { + alias '/media/'; + } + +} + diff --git a/requirements.txt b/requirements.txt index a72d46a29..49c37a21f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ djangorestframework==3.14.0 geopy~=2.3.0 rollbar~=0.16.3 psycopg2~=2.9.6 +gunicorn~=20.1.0 diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh deleted file mode 100755 index bab2c0b81..000000000 --- a/scripts/deploy_star_burger.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -e - -if [ "$EUID" -ne 0 ] - then - echo "Please run as root." - exit -fi - -git pull -npm ci --dev -npm audit fix -venv/bin/pip install -U -r requirements.txt -node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./" -venv/bin/python manage.py collectstatic --noinput -venv/bin/python manage.py migrate --noinput -systemctl restart starburger-django.service -systemctl reload nginx - -venv/bin/python scripts/report_deploy_rollbar.py - -echo "Project updated." diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh new file mode 100755 index 000000000..c7aeea7bf --- /dev/null +++ b/scripts/first_deploy.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +cp starburger.service.template /etc/systemd/system/starburger.service +systemctl daemon-reload +systemctl enable starburger +systemctl start starburger + +docker exec star-burger-django-1 python manage.py migrate +docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json + +venv/bin/python scripts/report_deploy_rollbar.py diff --git a/scripts/init_postgres.sh b/scripts/init_postgres.sh deleted file mode 100755 index 77481e978..000000000 --- a/scripts/init_postgres.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -if [ "$EUID" -ne 0 ] - then - echo "Please run as root." - exit -fi - -if [ $( docker ps -a | grep starburger-postgres | wc -l ) -gt 0 ] - then - echo "Postgres container already exists." - exit -fi - -apt-get install libpq-dev - -docker run -d --name starburger-postgres \ ---env-file .env -p 5432:5432 \ --v starburger_postgres:/var/lib/postgresql/data \ -postgres:14 -sleep 1 -venv/bin/python ./manage.py migrate -venv/bin/python ./manage.py loaddata data/db_dump.json -docker stop -t 10 starburger-postgres diff --git a/scripts/report_deploy_rollbar.py b/scripts/report_deploy_rollbar.py index 7e622cca7..a955c5e46 100644 --- a/scripts/report_deploy_rollbar.py +++ b/scripts/report_deploy_rollbar.py @@ -1,5 +1,3 @@ -"""Helper script to be used at the end of deploy_star_burger.sh""" - import subprocess import requests diff --git a/star_burger/settings.py b/star_burger/settings.py index f50f2a1fb..d40bb1d75 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -85,12 +85,22 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' -DATABASES = { - 'default': env.dj_db_url( - 'DB_URL', - default=f'sqlite:////{os.path.join(BASE_DIR, "db.sqlite3")}' - ) -} +if not env.str('POSTGRES_USER', None): + DEFAULT_DATABASE = { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +else: + DEFAULT_DATABASE = { + 'ENGINE': env.str('DB_ENGINE', 'django.db.backends.postgresql'), + 'NAME': env.str('POSTGRES_DB'), + 'USER': env.str('POSTGRES_USER'), + 'PASSWORD': env.str('POSTGRES_PASSWORD'), + 'HOST': env.str('POSTGRES_HOST', '127.0.0.1'), + 'PORT': env.str('POSTGRES_PORT', '5432'), + } + +DATABASES = {'default': DEFAULT_DATABASE} AUTH_PASSWORD_VALIDATORS = [ { diff --git a/starburger.service.template b/starburger.service.template new file mode 100644 index 000000000..13f2d0319 --- /dev/null +++ b/starburger.service.template @@ -0,0 +1,15 @@ +[Unit] +Description=Starburger Docker Compose App Service +Requires=docker.service +After=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/opt/star-burger/project +ExecStart=/usr/local/bin/docker-compose up -d +ExecStop=/usr/local/bin/docker-compose down +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target From 707c22c5ad450c195f2455771d5e7df57d1370a4 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Jul 2023 19:35:21 +0200 Subject: [PATCH 46/67] fix broken commands --- scripts/first_deploy.sh | 4 +++- starburger.service.template | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh index c7aeea7bf..943782bac 100755 --- a/scripts/first_deploy.sh +++ b/scripts/first_deploy.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + cp starburger.service.template /etc/systemd/system/starburger.service systemctl daemon-reload systemctl enable starburger @@ -8,4 +10,4 @@ systemctl start starburger docker exec star-burger-django-1 python manage.py migrate docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json -venv/bin/python scripts/report_deploy_rollbar.py +python scripts/report_deploy_rollbar.py diff --git a/starburger.service.template b/starburger.service.template index 13f2d0319..09a83b284 100644 --- a/starburger.service.template +++ b/starburger.service.template @@ -7,8 +7,8 @@ After=docker.service Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/star-burger/project -ExecStart=/usr/local/bin/docker-compose up -d -ExecStop=/usr/local/bin/docker-compose down +ExecStart=docker compose up -d +ExecStop=docker compose down TimeoutStartSec=0 [Install] From 89407b355779414d073d04665fd688cf3768e3a7 Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Jul 2023 19:40:38 +0200 Subject: [PATCH 47/67] fix broken path --- starburger.service.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starburger.service.template b/starburger.service.template index 09a83b284..68f0dbe41 100644 --- a/starburger.service.template +++ b/starburger.service.template @@ -6,7 +6,7 @@ After=docker.service [Service] Type=oneshot RemainAfterExit=yes -WorkingDirectory=/opt/star-burger/project +WorkingDirectory=/opt/star-burger/ ExecStart=docker compose up -d ExecStop=docker compose down TimeoutStartSec=0 From 581cbced7d2e1c2c9ca6510f2fca80d64ea7ee1b Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Jul 2023 23:14:05 +0200 Subject: [PATCH 48/67] add guard checks for missing components, move script call to container --- scripts/first_deploy.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh index 943782bac..e82d9819a 100755 --- a/scripts/first_deploy.sh +++ b/scripts/first_deploy.sh @@ -2,6 +2,16 @@ set -e +if [ ! -f data/db_dump.json ]; then + echo "Error: data/db_dump.json does not exist" + exit 1 +fi + +if [ ! -d media ] || [ ! "$(ls -A media)" ]; then + echo "Error: media directory does not exist or is empty" + exit 1 +fi + cp starburger.service.template /etc/systemd/system/starburger.service systemctl daemon-reload systemctl enable starburger @@ -10,4 +20,4 @@ systemctl start starburger docker exec star-burger-django-1 python manage.py migrate docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json -python scripts/report_deploy_rollbar.py +docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py From 4cbeaed97f53f735fa5bfe84c852d37233bd8dad Mon Sep 17 00:00:00 2001 From: mavel Date: Sun, 2 Jul 2023 23:36:31 +0200 Subject: [PATCH 49/67] add a deploy script --- scripts/deploy.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 scripts/deploy.sh diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 000000000..89af88abc --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +git pull +docker compose build +systemctl restart starburger +docker exec star-burger-django-1 python manage.py migrate --noinput + +docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py From 3b2899a4e0ca8be301b6b0dd803243a461f8be29 Mon Sep 17 00:00:00 2001 From: mavel Date: Mon, 3 Jul 2023 01:18:42 +0200 Subject: [PATCH 50/67] update README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e3bb4e45b..efa248467 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,10 @@ systemctl start starburger # запустить systemctl status starburger # посмотреть на статус и stdout ``` +### Подтянуть изменения из репозитория и перезапустить сервисы: +```sh +scripts/deploy.sh +``` ## Цели проекта From 1b240a0d4b51a47460f076d04359473aa569b202 Mon Sep 17 00:00:00 2001 From: mavel Date: Mon, 3 Jul 2023 01:19:14 +0200 Subject: [PATCH 51/67] update link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index efa248467..1c0dbd559 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Сайт доставки еды [Star Burger](https://starburger.mavel.cc/) +# Сайт доставки еды [Star Burger](http://starburger.mavel.cc/) Это сайт сети ресторанов Star Burger. Здесь можно заказать превосходные бургеры с доставкой на дом. From 7e2467950bec98c0f0f47d844f1db186a6ae73e5 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 00:26:38 +0200 Subject: [PATCH 52/67] certbot config --- docker-compose.yml | 15 +++++++ nginx.conf | 56 +++++++++++++++++-------- scripts/init_letsencrypt.sh | 83 +++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 scripts/init_letsencrypt.sh diff --git a/docker-compose.yml b/docker-compose.yml index f4300fcc7..72f329aa6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,7 @@ version: '3' services: db: + container_name: starburger_db image: postgres:14 environment: - POSTGRES_DB=${POSTGRES_DB} @@ -11,6 +12,7 @@ services: - postgres:/var/lib/postgresql/data django: + container_name: starburger_django build: . command: gunicorn -b 0.0.0.0:8081 --workers 3 star_burger.wsgi:application environment: @@ -26,16 +28,29 @@ services: - db nginx: + container_name: starburger_nginx image: nginx:1.25 ports: - "80:80" + - "443:443" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf - frontend:/frontend - media:/media + - ./data/certbot/conf:/etc/letsencrypt + - ./data/certbot/www:/var/www/certbot depends_on: - django + certbot: + container_name: starburger_certbot + image: certbot/certbot:v2.6.0 + volumes: + - ./data/certbot/conf:/etc/letsencrypt + - ./data/certbot/www:/var/www/certbot + depends_on: + - nginx + volumes: postgres: frontend: diff --git a/nginx.conf b/nginx.conf index 45ea4ae4f..7e069ba5d 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,21 +1,43 @@ server { - server_name starburger.mavel.cc www.starburger.mavel.cc; - listen 0:80; - - location / { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass http://django:8081/; - } - - location /static/ { - alias '/frontend/'; - } - - location /media/ { - alias '/media/'; - } + listen 80; + listen [::]:80; + + server_name starburger.mavel.cc www.starburger.mavel.cc; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + server_name starburger.mavel.cc www.starburger.mavel.cc; + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://django:8081/; + } + + location /static/ { + alias '/frontend/'; + } + + location /media/ { + alias '/media/'; + } } diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh new file mode 100644 index 000000000..febab402b --- /dev/null +++ b/scripts/init_letsencrypt.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +set -e + +if ! [ -x "$(command -v docker-compose)" ]; then + echo 'Error: docker-compose is not installed.' >&2 + exit 1 +fi + +domains=(starburger.mavel.cc www.starburger.mavel.cc) +rsa_key_size=4096 +data_path="./data/certbot" +email="starburger@mavel.cc" +staging=1 # Set to 1 if you're testing your setup to avoid hitting request limits +nginx_container_name="starburger_nginx" + +if [ -d "$data_path" ]; then + read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision + if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then + exit + fi +fi + + +if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then + echo "### Downloading recommended TLS parameters ..." + mkdir -p "$data_path/conf" + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf" + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem" + echo + fi + +echo "### Creating dummy certificate for $domains ..." +path="/etc/letsencrypt/live/$domains" +mkdir -p "$data_path/conf/live/$domains" +docker-compose run --rm --entrypoint "\ + openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\ + -keyout '$path/privkey.pem' \ + -out '$path/fullchain.pem' \ + -subj '/CN=localhost'" certbot +echo + + +echo "### Starting nginx ..." +docker-compose up --force-recreate -d nginx +echo + +echo "### Deleting dummy certificate for $domains ..." +docker-compose run --rm --entrypoint "\ + rm -Rf /etc/letsencrypt/live/$domains && \ + rm -Rf /etc/letsencrypt/archive/$domains && \ + rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot +echo + + +echo "### Requesting Let's Encrypt certificate for $domains ..." +#Join $domains to -d args +domain_args="" +for domain in "${domains[@]}"; do + domain_args="$domain_args -d $domain" +done + +# Select appropriate email arg +case "$email" in + "") email_arg="--register-unsafely-without-email" ;; + *) email_arg="--email $email" ;; +esac + +# Enable staging mode if needed +if [ $staging != "0" ]; then staging_arg="--staging"; fi + +docker-compose run --rm --entrypoint "\ + certbot certonly --webroot -w /var/www/certbot \ + $staging_arg \ + $email_arg \ + $domain_args \ + --rsa-key-size $rsa_key_size \ + --agree-tos \ + --force-renewal" certbot +echo + +echo "### Reloading nginx ..." +docker-compose exec $container_name nginx -s reload From eb079e2a31709a990dadb0e1dadd02b7cb0daff7 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 00:27:47 +0200 Subject: [PATCH 53/67] allow running script --- scripts/init_letsencrypt.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/init_letsencrypt.sh diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh old mode 100644 new mode 100755 From 6522ee0f91e9364512564ec869991bca9fa2663e Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 00:29:19 +0200 Subject: [PATCH 54/67] remove hyphen in docker compose --- scripts/init_letsencrypt.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh index febab402b..12aa0daac 100755 --- a/scripts/init_letsencrypt.sh +++ b/scripts/init_letsencrypt.sh @@ -2,8 +2,8 @@ set -e -if ! [ -x "$(command -v docker-compose)" ]; then - echo 'Error: docker-compose is not installed.' >&2 +if ! [ -x "$(command -v docker compose)" ]; then + echo 'Error: docker compose is not installed.' >&2 exit 1 fi @@ -33,7 +33,7 @@ if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ echo "### Creating dummy certificate for $domains ..." path="/etc/letsencrypt/live/$domains" mkdir -p "$data_path/conf/live/$domains" -docker-compose run --rm --entrypoint "\ +docker compose run --rm --entrypoint "\ openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\ -keyout '$path/privkey.pem' \ -out '$path/fullchain.pem' \ @@ -42,11 +42,11 @@ echo echo "### Starting nginx ..." -docker-compose up --force-recreate -d nginx +docker compose up --force-recreate -d nginx echo echo "### Deleting dummy certificate for $domains ..." -docker-compose run --rm --entrypoint "\ +docker compose run --rm --entrypoint "\ rm -Rf /etc/letsencrypt/live/$domains && \ rm -Rf /etc/letsencrypt/archive/$domains && \ rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot @@ -69,7 +69,7 @@ esac # Enable staging mode if needed if [ $staging != "0" ]; then staging_arg="--staging"; fi -docker-compose run --rm --entrypoint "\ +docker compose run --rm --entrypoint "\ certbot certonly --webroot -w /var/www/certbot \ $staging_arg \ $email_arg \ @@ -80,4 +80,4 @@ docker-compose run --rm --entrypoint "\ echo echo "### Reloading nginx ..." -docker-compose exec $container_name nginx -s reload +docker compose exec $container_name nginx -s reload From eb51ba028c60cd0f19366d0ec041ab36fd6ac0a8 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 00:42:13 +0200 Subject: [PATCH 55/67] remove user input check --- scripts/init_letsencrypt.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh index 12aa0daac..c01abafa6 100755 --- a/scripts/init_letsencrypt.sh +++ b/scripts/init_letsencrypt.sh @@ -76,7 +76,8 @@ docker compose run --rm --entrypoint "\ $domain_args \ --rsa-key-size $rsa_key_size \ --agree-tos \ - --force-renewal" certbot + --force-renewal \ + --no-eff-email" certbot echo echo "### Reloading nginx ..." From 5d5655358ebf4ffadf9967c71447c306358ac9a7 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 00:46:16 +0200 Subject: [PATCH 56/67] add allow all for certbot challenge --- nginx.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx.conf b/nginx.conf index 7e069ba5d..eec1a4f30 100644 --- a/nginx.conf +++ b/nginx.conf @@ -6,6 +6,7 @@ server { location /.well-known/acme-challenge/ { root /var/www/certbot; + allow all; } location / { From 3ea9651699683a10aa21b62225826e98557e7944 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 00:54:58 +0200 Subject: [PATCH 57/67] replace domain from example --- nginx.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx.conf b/nginx.conf index eec1a4f30..5b56f0d99 100644 --- a/nginx.conf +++ b/nginx.conf @@ -18,8 +18,8 @@ server { listen 443 ssl; listen [::]:443 ssl; http2 on; - ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem; + ssl_certificate /etc/letsencrypt/live/starburger.mavel.cc/fullchain.pem; # replace domain here + ssl_certificate_key /etc/letsencrypt/live/starburger.mavel.cc/privkey.pem; # and here include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; From b015c329547d75ab49982bbcfcfc268c42cc4e31 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 01:31:40 +0200 Subject: [PATCH 58/67] certbot config and renewal services --- scripts/deploy.sh | 8 ++++---- scripts/first_deploy.sh | 12 ++++++------ scripts/init_letsencrypt.sh | 4 ++-- systemd_units/starburger.target | 7 +++++++ systemd_units/starburger_cert_renewal.service | 7 +++++++ systemd_units/starburger_cert_renewal.timer | 10 ++++++++++ systemd_units/starburger_clearsessions.service | 6 ++++++ systemd_units/starburger_clearsessions.timer | 10 ++++++++++ .../starburger_containers.service | 1 + 9 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 systemd_units/starburger.target create mode 100644 systemd_units/starburger_cert_renewal.service create mode 100644 systemd_units/starburger_cert_renewal.timer create mode 100644 systemd_units/starburger_clearsessions.service create mode 100644 systemd_units/starburger_clearsessions.timer rename starburger.service.template => systemd_units/starburger_containers.service (92%) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 89af88abc..3adc64642 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -3,8 +3,8 @@ set -e git pull -docker compose build -systemctl restart starburger -docker exec star-burger-django-1 python manage.py migrate --noinput +docker compose build django +docker compose restart django +docker exec starburger_django python manage.py migrate --noinput -docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py +docker exec starburger_django python scripts/report_deploy_rollbar.py diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh index e82d9819a..270718978 100755 --- a/scripts/first_deploy.sh +++ b/scripts/first_deploy.sh @@ -12,12 +12,12 @@ if [ ! -d media ] || [ ! "$(ls -A media)" ]; then exit 1 fi -cp starburger.service.template /etc/systemd/system/starburger.service +cp systemd_units/* /etc/systemd/system/ systemctl daemon-reload -systemctl enable starburger -systemctl start starburger +systemctl enable starburger.target +systemctl start starburger.target -docker exec star-burger-django-1 python manage.py migrate -docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json +docker exec starburger_django python manage.py migrate +docker exec starburger_django python manage.py loaddata data/db_dump.json -docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py +docker exec starburger_django python scripts/report_deploy_rollbar.py diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh index c01abafa6..6f79ed4ae 100755 --- a/scripts/init_letsencrypt.sh +++ b/scripts/init_letsencrypt.sh @@ -11,7 +11,7 @@ domains=(starburger.mavel.cc www.starburger.mavel.cc) rsa_key_size=4096 data_path="./data/certbot" email="starburger@mavel.cc" -staging=1 # Set to 1 if you're testing your setup to avoid hitting request limits +staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits nginx_container_name="starburger_nginx" if [ -d "$data_path" ]; then @@ -81,4 +81,4 @@ docker compose run --rm --entrypoint "\ echo echo "### Reloading nginx ..." -docker compose exec $container_name nginx -s reload +docker compose exec "$container_name" nginx -s reload diff --git a/systemd_units/starburger.target b/systemd_units/starburger.target new file mode 100644 index 000000000..e5e3a4df3 --- /dev/null +++ b/systemd_units/starburger.target @@ -0,0 +1,7 @@ +[Unit] +Description=Starburger Target +Requires=starburger_containers.service starburger_cert_renewal.timer starburger_clearsessions.timer +After=starburger_containers.service starburger_cert_renewal.timer starburger_clearsessions.timer + +[Install] +WantedBy=multi-user.target diff --git a/systemd_units/starburger_cert_renewal.service b/systemd_units/starburger_cert_renewal.service new file mode 100644 index 000000000..aa2a3b29a --- /dev/null +++ b/systemd_units/starburger_cert_renewal.service @@ -0,0 +1,7 @@ +[Unit] +Description=Starburger Certbot Renewal + +[Service] +WorkingDirectory=/opt/star-burger +ExecStart=/usr/bin/docker exec starburger_certbot certbot renew --force-renewal +ExecStartPost=/usr/bin/docker exec starburger_nginx nginx -s reload diff --git a/systemd_units/starburger_cert_renewal.timer b/systemd_units/starburger_cert_renewal.timer new file mode 100644 index 000000000..7ab97b48c --- /dev/null +++ b/systemd_units/starburger_cert_renewal.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Weekly Starburger Certbot Renewal +PartOf=starburger.target + +[Timer] +OnCalendar=weekly +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/systemd_units/starburger_clearsessions.service b/systemd_units/starburger_clearsessions.service new file mode 100644 index 000000000..e96c63aa0 --- /dev/null +++ b/systemd_units/starburger_clearsessions.service @@ -0,0 +1,6 @@ +[Unit] +Description=Starburger Django Clear Sessions + +[Service] +WorkingDirectory=/opt/star-burger +ExecStart=/usr/bin/docker exec starburger_django python manage.py clearsessions diff --git a/systemd_units/starburger_clearsessions.timer b/systemd_units/starburger_clearsessions.timer new file mode 100644 index 000000000..18d469256 --- /dev/null +++ b/systemd_units/starburger_clearsessions.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Weekly Django Clear Sessions for Starburger +PartOf=starburger.target + +[Timer] +OnCalendar=weekly +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/starburger.service.template b/systemd_units/starburger_containers.service similarity index 92% rename from starburger.service.template rename to systemd_units/starburger_containers.service index 68f0dbe41..9f5ead134 100644 --- a/starburger.service.template +++ b/systemd_units/starburger_containers.service @@ -2,6 +2,7 @@ Description=Starburger Docker Compose App Service Requires=docker.service After=docker.service +PartOf=starburger.target [Service] Type=oneshot From 0c3460ddd55ee6d010a3de4e8d7a21f4ac2f9664 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 01:36:42 +0200 Subject: [PATCH 59/67] add certbot script call to first_deploy script --- scripts/first_deploy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh index 270718978..60aba13f4 100755 --- a/scripts/first_deploy.sh +++ b/scripts/first_deploy.sh @@ -12,6 +12,8 @@ if [ ! -d media ] || [ ! "$(ls -A media)" ]; then exit 1 fi +scripts/init_letsencrypt.sh + cp systemd_units/* /etc/systemd/system/ systemctl daemon-reload systemctl enable starburger.target From 23f9c33ced531c15c58c3b90b81933d9d8af820c Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 01:45:42 +0200 Subject: [PATCH 60/67] fix error in variable name --- scripts/init_letsencrypt.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh index 6f79ed4ae..59d049c8a 100755 --- a/scripts/init_letsencrypt.sh +++ b/scripts/init_letsencrypt.sh @@ -81,4 +81,4 @@ docker compose run --rm --entrypoint "\ echo echo "### Reloading nginx ..." -docker compose exec "$container_name" nginx -s reload +docker compose exec "$nginx_container_name" nginx -s reload From 17a8e033e72ae60a66a7c1ce6b76a7b61ae24e21 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 11:36:23 +0200 Subject: [PATCH 61/67] use service name instead of container name --- scripts/init_letsencrypt.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh index 59d049c8a..37c788016 100755 --- a/scripts/init_letsencrypt.sh +++ b/scripts/init_letsencrypt.sh @@ -12,7 +12,6 @@ rsa_key_size=4096 data_path="./data/certbot" email="starburger@mavel.cc" staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits -nginx_container_name="starburger_nginx" if [ -d "$data_path" ]; then read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision @@ -81,4 +80,4 @@ docker compose run --rm --entrypoint "\ echo echo "### Reloading nginx ..." -docker compose exec "$nginx_container_name" nginx -s reload +docker compose exec nginx nginx -s reload From 80fb803d453a5069beee4065381adda462726152 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 11:50:15 +0200 Subject: [PATCH 62/67] remove existing certbot data_dir to avoid filepath problems with nginx --- scripts/init_letsencrypt.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh index 37c788016..bcaaa5621 100755 --- a/scripts/init_letsencrypt.sh +++ b/scripts/init_letsencrypt.sh @@ -20,6 +20,8 @@ if [ -d "$data_path" ]; then fi fi +rm -r "$data_path" + if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then echo "### Downloading recommended TLS parameters ..." From 43b8685a891579e0413c42b2f3791aa184198d19 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 11:50:30 +0200 Subject: [PATCH 63/67] echo message at the end --- scripts/first_deploy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh index 60aba13f4..4200d2bf0 100755 --- a/scripts/first_deploy.sh +++ b/scripts/first_deploy.sh @@ -23,3 +23,5 @@ docker exec starburger_django python manage.py migrate docker exec starburger_django python manage.py loaddata data/db_dump.json docker exec starburger_django python scripts/report_deploy_rollbar.py + +echo "Deploy successful." From 0e2b4453d89f0c23f66ce673d8c31a7214be91f5 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 12:17:22 +0200 Subject: [PATCH 64/67] take domains and email from .env --- docker-compose.yml | 1 - scripts/init_letsencrypt.sh | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 72f329aa6..70d924739 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,6 @@ services: command: gunicorn -b 0.0.0.0:8081 --workers 3 star_burger.wsgi:application environment: - POSTGRES_HOST=db - - ALLOWED_HOSTS=127.0.0.1,starburger.mavel.cc volumes: - .:/app - frontend:/app/frontend diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh index bcaaa5621..f71a9d3ca 100755 --- a/scripts/init_letsencrypt.sh +++ b/scripts/init_letsencrypt.sh @@ -7,10 +7,12 @@ if ! [ -x "$(command -v docker compose)" ]; then exit 1 fi -domains=(starburger.mavel.cc www.starburger.mavel.cc) +source .env + +IFS=',' read -ra domains <<< "$CERT_DOMAINS" # stores `CERT_DOMAINS` from .env in the array `domains` rsa_key_size=4096 data_path="./data/certbot" -email="starburger@mavel.cc" +email="$EMAIL" staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits if [ -d "$data_path" ]; then From 483c3c24193954d3e43c1a603d37a12e4658d548 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 12:17:34 +0200 Subject: [PATCH 65/67] update README --- README.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1c0dbd559..638648b84 100644 --- a/README.md +++ b/README.md @@ -147,32 +147,45 @@ Parcel будет следить за файлами в каталоге `bundle - `DEBUG` — дебаг-режим. Поставьте `False`. - `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте. - `ALLOWED_HOSTS` — [см. документацию Django](https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts) -- `POSTGRES_USER=...` -- `POSTGRES_PASSWORD=...` -- `POSTGRES_DB=...` +- `POSTGRES_USER` +- `POSTGRES_PASSWORD` +- `POSTGRES_DB` + +Для SSL-сертификата: +- `EMAIL` +- `CERT_DOMAINS` - список доменов в формате `example.org,www.example.org` Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД. Убедиться, что на сервере установлен Docker. +Заменить домены в `nginx.conf` на ваши собственные. + Чтобы получать мгновенные уведомления об ошибках, подключите свой аккаунт [Rollbar](https://docs.rollbar.com/docs/setup) и добавьте следующие переменные в `.env`: -- `ROLLBAR_TOKEN=...` -- `ROLLBAR_ENV=...` - development/production/... -- `ROLLBAR_USERNAME=...` +- `ROLLBAR_TOKEN` +- `ROLLBAR_ENV` - `development`/`production`/... +- `ROLLBAR_USERNAME` + +### Поднять контейнеры, получить сертификат SSL и запустить приложение: -### Поднять контейнеры и запустить приложение: ```sh scripts/first_deploy.sh ``` -Приложение контролируется systemd и запускается автоматически при перезагрузке сервера. +Приложение контролируется таргетом systemd и запускается автоматически при перезагрузке сервера. +В таргет включены следующие юниты: +- starburger_containers.service - запускает и останавливает контейнеры через docker compose +- starburger_cert_renewal.timer - обновляет сертификат SSL и перезагружает nginx +- starburger_clearsessions.timer - удаляет устаревшие сессии в Django + Команды, которые могут пригодиться: ```sh -systemctl stop starburger # остановить -systemctl start starburger # запустить -systemctl status starburger # посмотреть на статус и stdout +systemctl stop starburger.target # остановить +systemctl start starburger.target # запустить +docker compose logs # посмотреть на stdout контейнеров ``` + ### Подтянуть изменения из репозитория и перезапустить сервисы: ```sh scripts/deploy.sh From a5e07e06378e2a20b1e1982cbf381ecf875ebdcc Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 12:17:55 +0200 Subject: [PATCH 66/67] add comments for domains --- nginx.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx.conf b/nginx.conf index 5b56f0d99..bfc8cac2f 100644 --- a/nginx.conf +++ b/nginx.conf @@ -2,7 +2,7 @@ server { listen 80; listen [::]:80; - server_name starburger.mavel.cc www.starburger.mavel.cc; + server_name starburger.mavel.cc www.starburger.mavel.cc; # replace domain here location /.well-known/acme-challenge/ { root /var/www/certbot; @@ -23,7 +23,7 @@ server { include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; - server_name starburger.mavel.cc www.starburger.mavel.cc; + server_name starburger.mavel.cc www.starburger.mavel.cc; # replace domain here location / { proxy_set_header Host $host; From cd87f75c8a553cf66c39c5d60d5edc5581795dd6 Mon Sep 17 00:00:00 2001 From: mavel Date: Thu, 6 Jul 2023 12:21:55 +0200 Subject: [PATCH 67/67] fix an error in path joining --- star_burger/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/star_burger/settings.py b/star_burger/settings.py index d40bb1d75..626c66e23 100644 --- a/star_burger/settings.py +++ b/star_burger/settings.py @@ -88,7 +88,7 @@ if not env.str('POSTGRES_USER', None): DEFAULT_DATABASE = { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } else: DEFAULT_DATABASE = {