diff --git a/.gitignore b/.gitignore
index 002453b..0854dcd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,5 +126,11 @@ web_modules/
config.yaml
*.db
+# GimVicUrnik Firebase credentials
+firebasecredentials.json
+
+# Firebase service worker
+firebase-messaging-sw.js
+
# Do not ignore .gitkeep files
!.gitkeep
diff --git a/API/gimvicurnik/__init__.py b/API/gimvicurnik/__init__.py
index 180cb88..77ae613 100644
--- a/API/gimvicurnik/__init__.py
+++ b/API/gimvicurnik/__init__.py
@@ -16,6 +16,7 @@
FeedHandler,
ListHandler,
MenusHandler,
+ NotificationsHandler,
ScheduleHandler,
SubstitutionsHandler,
TimetableHandler,
@@ -27,6 +28,7 @@
update_solsis_command,
cleanup_database_command,
update_timetable_command,
+ handle_notifications_command,
)
from .config import Config
from .database import Session, SessionFactory
@@ -250,6 +252,7 @@ def register_commands(self) -> None:
self.app.cli.add_command(update_solsis_command)
self.app.cli.add_command(cleanup_database_command)
self.app.cli.add_command(create_database_command)
+ self.app.cli.add_command(handle_notifications_command)
def register_routes(self) -> None:
"""Register all application routes."""
@@ -258,6 +261,7 @@ def register_routes(self) -> None:
TimetableHandler.register(self.app, self.config)
SubstitutionsHandler.register(self.app, self.config)
MenusHandler.register(self.app, self.config)
+ NotificationsHandler.register(self.app, self.config)
ScheduleHandler.register(self.app, self.config)
DocumentsHandler.register(self.app, self.config)
FeedHandler.register(self.app, self.config)
diff --git a/API/gimvicurnik/__main__.py b/API/gimvicurnik/__main__.py
index 4278543..d42e67c 100644
--- a/API/gimvicurnik/__main__.py
+++ b/API/gimvicurnik/__main__.py
@@ -44,6 +44,7 @@ def _get_version(ctx: click.Context, _param: str, value: str) -> None:
pdfminer_version = metadata.version("pdfminer.six")
openpyxl_version = metadata.version("openpyxl")
mammoth_version = metadata.version("mammoth")
+ firebase_admin_version = metadata.version("firebase-admin")
try:
sentry_version = metadata.version("sentry-sdk")
@@ -61,6 +62,7 @@ def _get_version(ctx: click.Context, _param: str, value: str) -> None:
f"pdfminer: {pdfminer_version}\n"
f"openpyxl: {openpyxl_version}\n"
f"mammoth: {mammoth_version}\n"
+ f"firebase admin: {firebase_admin_version}\n"
f"Sentry SDK: {sentry_version}",
color=ctx.color,
)
diff --git a/API/gimvicurnik/blueprints/__init__.py b/API/gimvicurnik/blueprints/__init__.py
index b92d4ee..6962aa1 100644
--- a/API/gimvicurnik/blueprints/__init__.py
+++ b/API/gimvicurnik/blueprints/__init__.py
@@ -3,6 +3,7 @@
from .feed import FeedHandler
from .list import ListHandler
from .menus import MenusHandler
+from .notifications import NotificationsHandler
from .schedule import ScheduleHandler
from .substitutions import SubstitutionsHandler
from .timetable import TimetableHandler
diff --git a/API/gimvicurnik/blueprints/notifications.py b/API/gimvicurnik/blueprints/notifications.py
new file mode 100644
index 0000000..5e47165
--- /dev/null
+++ b/API/gimvicurnik/blueprints/notifications.py
@@ -0,0 +1,43 @@
+from __future__ import annotations
+
+import typing
+
+from .base import BaseHandler
+from ..database import Notification, Session
+
+if typing.TYPE_CHECKING:
+ from typing import Any
+ from flask import Blueprint
+ from ..config import Config
+
+
+class NotificationsHandler(BaseHandler):
+ name = "notifications"
+
+ @classmethod
+ def routes(cls, bp: Blueprint, config: Config) -> None:
+ @bp.route("/notifications")
+ def get_notifications() -> list[dict[str, Any]]:
+ # fmt: off
+ query = (
+ Session.query(
+ Notification.date,
+ Notification.title,
+ Notification.content,
+ Notification.visible
+ )
+ .order_by(
+ Notification.date,
+ )
+ )
+ # fmt: on
+
+ return [
+ {
+ "date": notification.date.isoformat(),
+ "title": notification.title,
+ "content": notification.content,
+ }
+ for notification in query
+ if notification.visible
+ ]
diff --git a/API/gimvicurnik/commands/__init__.py b/API/gimvicurnik/commands/__init__.py
index 72a290b..d1169a8 100644
--- a/API/gimvicurnik/commands/__init__.py
+++ b/API/gimvicurnik/commands/__init__.py
@@ -9,6 +9,7 @@
from ..database import Base, SessionFactory, Document, DocumentType
from ..updaters import EClassroomUpdater, MenuUpdater, TimetableUpdater, SolsisUpdater
+from ..notifications import PushNotificationsHandler
from ..utils.sentry import with_transaction
if typing.TYPE_CHECKING:
@@ -122,3 +123,46 @@ def create_database_command(ctx: click.Context, recreate: bool) -> None:
logging.getLogger(__name__).info("Creating the database")
Base.metadata.create_all(gimvicurnik.engine)
+
+
+@click.command("handle-notifications", help="Handle the notifications.")
+@click.option("--immediate-substitutions", help="Send immediate substitutions.", is_flag=True)
+@click.option("--scheduled-substitutions", help="Send scheduled substitutions.", is_flag=True)
+@click.option("--circulars", help="Send circulars.", is_flag=True)
+@click.option("--menu", help="Send menus.", is_flag=True)
+@click.option("--cleanup-users", help="Cleanup probably inactive users from the store.", is_flag=True)
+@with_transaction(name="handle-notifications", op="command")
+def handle_notifications_command(
+ immediate_substitutions: bool,
+ scheduled_substitutions: bool,
+ circulars: bool,
+ menu: bool,
+ cleanup_users: bool,
+) -> None:
+ """Handle the notifications."""
+
+ logging.getLogger(__name__).info("Handling notifications")
+
+ with SessionFactory.begin() as session:
+ gimvicurnik: GimVicUrnik = current_app.config["GIMVICURNIK"]
+
+ notifications = PushNotificationsHandler(
+ gimvicurnik.config.firebase, gimvicurnik.config.urls, session
+ )
+
+ if not any([immediate_substitutions, scheduled_substitutions, circulars, menu, cleanup_users]):
+ notifications.send_immediate_substitutions_notifications()
+ notifications.send_scheduled_substitutions_notifications()
+ notifications.send_circulars_notifications()
+ notifications.send_menu_notifications()
+ else:
+ if immediate_substitutions:
+ notifications.send_immediate_substitutions_notifications()
+ if scheduled_substitutions:
+ notifications.send_scheduled_substitutions_notifications()
+ if circulars:
+ notifications.send_circulars_notifications()
+ if menu:
+ notifications.send_menu_notifications()
+ if cleanup_users:
+ notifications.cleanup_users()
diff --git a/API/gimvicurnik/config/__init__.py b/API/gimvicurnik/config/__init__.py
index c38993a..7595af0 100644
--- a/API/gimvicurnik/config/__init__.py
+++ b/API/gimvicurnik/config/__init__.py
@@ -67,6 +67,20 @@ class ConfigURLs:
api: str
+# --------- FIREBASE CONFIG ------
+
+
+@define(kw_only=True)
+class ConfigFirebase:
+ apiKey: str
+ authDomain: str
+ projectId: str
+ storageBucket: str
+ messagingSenderId: int
+ appId: str
+ vapidKey: str
+
+
# -------- SENTRY CONFIG ---------
@@ -115,6 +129,7 @@ class Config:
urls: ConfigURLs
database: str
cors: list[str] = Factory(list)
+ firebase: ConfigFirebase
sentry: ConfigSentry | None = None
logging: dict | str | None = field(default=None, converter=_identity_convertor)
lessonTimes: list[ConfigLessonTime]
diff --git a/API/gimvicurnik/database/__init__.py b/API/gimvicurnik/database/__init__.py
index b84504b..36032b4 100644
--- a/API/gimvicurnik/database/__init__.py
+++ b/API/gimvicurnik/database/__init__.py
@@ -291,3 +291,16 @@ class LunchMenu(Base):
normal: Mapped[text | None]
vegetarian: Mapped[text | None]
+
+
+class Notification(Base):
+ __tablename__ = "notifications"
+
+ id: Mapped[intpk]
+
+ date: Mapped[date_] = mapped_column(index=True)
+
+ title: Mapped[text]
+ content: Mapped[longtext | None]
+
+ visible: Mapped[bool]
diff --git a/API/gimvicurnik/errors/__init__.py b/API/gimvicurnik/errors/__init__.py
index 006d7d6..3d7531e 100644
--- a/API/gimvicurnik/errors/__init__.py
+++ b/API/gimvicurnik/errors/__init__.py
@@ -10,5 +10,6 @@
LunchScheduleFormatError,
)
from .menu import MenuApiError, MenuDateError, MenuFormatError
+from .notifications import NotificationsFirestoreError
from .solsis import SolsisApiError
from .timetable import TimetableApiError
diff --git a/API/gimvicurnik/errors/notifications.py b/API/gimvicurnik/errors/notifications.py
new file mode 100644
index 0000000..0df976c
--- /dev/null
+++ b/API/gimvicurnik/errors/notifications.py
@@ -0,0 +1,5 @@
+from .base import GimVicUrnikError
+
+
+class NotificationsFirestoreError(GimVicUrnikError):
+ pass
diff --git a/API/gimvicurnik/notifications/__init__.py b/API/gimvicurnik/notifications/__init__.py
new file mode 100644
index 0000000..444ef5f
--- /dev/null
+++ b/API/gimvicurnik/notifications/__init__.py
@@ -0,0 +1,187 @@
+from __future__ import annotations
+
+import logging
+import typing
+import requests
+import json
+from datetime import datetime, timedelta
+
+from sqlalchemy import and_
+
+import firebase_admin # type: ignore
+from firebase_admin import credentials, firestore
+from google.cloud.firestore import FieldFilter, And # type: ignore
+
+from ..database import DocumentType, Document
+from ..errors import NotificationsFirestoreError
+from ..utils.notifications import NotificationType, get_page_name
+from ..utils.sentry import sentry_available
+
+if typing.TYPE_CHECKING:
+ from typing import Any
+ from ..config import ConfigFirebase, ConfigURLs
+ from sqlalchemy.orm import Session
+
+
+class PushNotificationsHandler:
+ def __init__(self, config_firebase: ConfigFirebase, config_urls: ConfigURLs, session: Session) -> None:
+ self.logger = logging.getLogger("notifications")
+ self.requests = requests.Session()
+ self.config_firebase = config_firebase
+ self.config_urls = config_urls
+ self.session = session
+
+ self.credentials = credentials.Certificate("firebasecredentials.json")
+ firebase_admin.initialize_app(self.credentials)
+
+ self.db = firestore.client()
+
+ def _get_user_tokens(self, field: str) -> list[str]:
+ try:
+ users = self.db.collection("users").where(filter=FieldFilter(field, "==", True)).stream()
+ except Exception as error:
+ raise NotificationsFirestoreError("Error while filtering users collection") from error
+
+ return [user.id for user in users]
+
+ def _get_documents_data(self, type: DocumentType, time_span: int) -> list[dict[str, Any]]:
+ # Filter specified documents that are not older than the specified time
+ query = (
+ self.session.query(Document)
+ .filter(
+ and_(
+ Document.type == type,
+ Document.created > datetime.now().date() - timedelta(minutes=time_span),
+ )
+ )
+ .order_by(Document.created)
+ )
+
+ # Return only the useful data
+ match type:
+ case DocumentType.CIRCULAR:
+ return [
+ {
+ "title": circular.title,
+ }
+ for circular in query
+ if circular.title
+ ]
+ case DocumentType.SNACK_MENU | DocumentType.LUNCH_MENU:
+ return [
+ {
+ "start": menu.effective.strftime("%d. %m. %Y"),
+ "end": (menu.effective + timedelta(days=4)).strftime("%d. %m. %Y"),
+ }
+ for menu in query
+ if menu.effective
+ ]
+
+ return []
+
+ def _send_message(self, type: NotificationType, token: str, title: str, body: str) -> None:
+ url = f"https://fcm.googleapis.com/v1/projects/{self.config_firebase.projectId}/messages:send"
+
+ message = {
+ "message": {
+ "token": token,
+ "data": {
+ "title": title,
+ "body": body,
+ "link_url": self.config_urls.website + get_page_name(type),
+ },
+ }
+ }
+
+ access_token = self.credentials.get_access_token().access_token
+
+ headers = {"Authorization": f"Bearer {access_token}", "Content-Type": "application/json"}
+
+ response_json = self.requests.post(url, headers=headers, data=json.dumps(message)).json()
+
+ if "error" in response_json:
+ match response_json["error"]["code"]:
+ case 404:
+ # UNREGISTERED
+ # This usually means that the token used is no longer valid, so we can delete it
+
+ self.db.collection("users").document(token).delete()
+ case 400:
+ # INVALID_ARGUMENT
+ # Request parameters were invalid
+
+ if sentry_available:
+ import sentry_sdk
+
+ # fmt: off
+ sentry_sdk.set_context("notification", {
+ "token": token,
+ "title": title,
+ "body": body,
+ })
+ # fmt: on
+
+ sentry_sdk.set_tag("notification_type", type.value)
+
+ self.logger.info("FCM request parameters were invalid")
+
+ def send_immediate_substitutions_notifications(self) -> None:
+ pass
+
+ def send_scheduled_substitutions_notifications(self) -> None:
+ pass
+
+ def send_circulars_notifications(self) -> None:
+ tokens = self._get_user_tokens("circularsNotificationsEnabled")
+ circulars = self._get_documents_data(DocumentType.CIRCULAR, 15)
+
+ for token in tokens:
+ for circular in circulars:
+ self._send_message(NotificationType.CIRCULAR, token, "Nova okrožnica", f'{circular["title"]}')
+
+ def send_menu_notifications(self) -> None:
+ tokens = self._get_user_tokens("snackMenuNotificationsEnabled")
+ snack_menus = self._get_documents_data(DocumentType.SNACK_MENU, 15)
+ lunch_menus = self._get_documents_data(DocumentType.LUNCH_MENU, 15)
+
+ for token in tokens:
+ for menu in snack_menus:
+ self._send_message(
+ NotificationType.SNACK_MENU,
+ token,
+ "Jedilnik za malico",
+ f'{menu["start"]} - {menu["end"]}',
+ )
+
+ for menu in lunch_menus:
+ self._send_message(
+ NotificationType.LUNCH_MENU,
+ token,
+ "Jedilnik za kosilo",
+ f'{menu["start"]} - {menu["end"]}',
+ )
+
+ def cleanup_users(self) -> None:
+ try:
+ # Users that do not have any notifications enabled are probably stale
+ users = (
+ self.db.collection("users")
+ .where(
+ filter=And(
+ [
+ FieldFilter("immediateSubstitutionsNotificationsEnabled", "==", False),
+ FieldFilter("scheduledSubstitutionsNotificationsEnabled", "==", False),
+ FieldFilter("circularsNotificationsEnabled", "==", False),
+ FieldFilter("snackMenuNotificationsEnabled", "==", False),
+ FieldFilter("lunchMenuNotificationsEnabled", "==", False),
+ ]
+ )
+ )
+ .stream()
+ )
+ except Exception as error:
+ raise NotificationsFirestoreError("Error while filtering users collection") from error
+
+ for user in users:
+ self.db.collection("users").document(user.id).delete()
+ self.logger.info("Deleted user from firestore:", user.id)
diff --git a/API/gimvicurnik/utils/notifications.py b/API/gimvicurnik/utils/notifications.py
new file mode 100644
index 0000000..cff9ee4
--- /dev/null
+++ b/API/gimvicurnik/utils/notifications.py
@@ -0,0 +1,22 @@
+from __future__ import annotations
+
+import enum
+
+
+@enum.unique
+class NotificationType(enum.Enum):
+ IMMEDIATE_SUBSTITUTION = "immediate-substitution"
+ SCHEDULED_SUBSTITUTION = "scheduled-substitution"
+ CIRCULAR = "circular"
+ LUNCH_MENU = "lunch-menu"
+ SNACK_MENU = "snack-menu"
+
+
+def get_page_name(type: NotificationType) -> str:
+ match type:
+ case NotificationType.IMMEDIATE_SUBSTITUTION | NotificationType.SCHEDULED_SUBSTITUTION:
+ return "timetable"
+ case NotificationType.CIRCULAR:
+ return "circulars"
+ case NotificationType.LUNCH_MENU | NotificationType.SNACK_MENU:
+ return "menu"
diff --git a/API/poetry.lock b/API/poetry.lock
index 9cf54bf..0558d95 100644
--- a/API/poetry.lock
+++ b/API/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "asttokens"
@@ -67,6 +67,37 @@ files = [
{file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
]
+[[package]]
+name = "cachecontrol"
+version = "0.14.2"
+description = "httplib2 caching for requests"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cachecontrol-0.14.2-py3-none-any.whl", hash = "sha256:ebad2091bf12d0d200dfc2464330db638c5deb41d546f6d7aca079e87290f3b0"},
+ {file = "cachecontrol-0.14.2.tar.gz", hash = "sha256:7d47d19f866409b98ff6025b6a0fca8e4c791fb31abbd95f622093894ce903a2"},
+]
+
+[package.dependencies]
+msgpack = ">=0.5.2,<2.0.0"
+requests = ">=2.16.0"
+
+[package.extras]
+dev = ["CacheControl[filecache,redis]", "build", "cherrypy", "codespell[tomli]", "furo", "mypy", "pytest", "pytest-cov", "ruff", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"]
+filecache = ["filelock (>=3.8.0)"]
+redis = ["redis (>=2.10.5)"]
+
+[[package]]
+name = "cachetools"
+version = "5.5.1"
+description = "Extensible memoizing collections and decorators"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"},
+ {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"},
+]
+
[[package]]
name = "cattrs"
version = "24.1.2"
@@ -408,6 +439,25 @@ files = [
[package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
+[[package]]
+name = "firebase-admin"
+version = "6.6.0"
+description = "Firebase Admin Python SDK"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "firebase_admin-6.6.0-py3-none-any.whl", hash = "sha256:4a55af17e0b4e3de05ba43ad25d92af29b48263622a913faea66e9c8ba50aeef"},
+ {file = "firebase_admin-6.6.0.tar.gz", hash = "sha256:9ce851ae71038acaaeffdcfc09b175dadc3b2e5aa8a0b65760e21bb10253b684"},
+]
+
+[package.dependencies]
+cachecontrol = ">=0.12.14"
+google-api-core = {version = ">=1.22.1,<3.0.0dev", extras = ["grpc"], markers = "platform_python_implementation != \"PyPy\""}
+google-api-python-client = ">=1.7.8"
+google-cloud-firestore = {version = ">=2.19.0", markers = "platform_python_implementation != \"PyPy\""}
+google-cloud-storage = ">=1.37.1"
+pyjwt = {version = ">=2.5.0", extras = ["crypto"]}
+
[[package]]
name = "flask"
version = "3.1.0"
@@ -430,6 +480,234 @@ Werkzeug = ">=3.1"
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
+[[package]]
+name = "google-api-core"
+version = "2.24.1"
+description = "Google API client core library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "google_api_core-2.24.1-py3-none-any.whl", hash = "sha256:bc78d608f5a5bf853b80bd70a795f703294de656c096c0968320830a4bc280f1"},
+ {file = "google_api_core-2.24.1.tar.gz", hash = "sha256:f8b36f5456ab0dd99a1b693a40a31d1e7757beea380ad1b38faaf8941eae9d8a"},
+]
+
+[package.dependencies]
+google-auth = ">=2.14.1,<3.0.dev0"
+googleapis-common-protos = ">=1.56.2,<2.0.dev0"
+grpcio = [
+ {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""},
+ {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
+]
+grpcio-status = [
+ {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""},
+ {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
+]
+proto-plus = [
+ {version = ">=1.22.3,<2.0.0dev", markers = "python_version < \"3.13\""},
+ {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""},
+]
+protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0"
+requests = ">=2.18.0,<3.0.0.dev0"
+
+[package.extras]
+async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"]
+grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"]
+grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
+grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
+
+[[package]]
+name = "google-api-python-client"
+version = "2.161.0"
+description = "Google API Client Library for Python"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "google_api_python_client-2.161.0-py2.py3-none-any.whl", hash = "sha256:9476a5a4f200bae368140453df40f9cda36be53fa7d0e9a9aac4cdb859a26448"},
+ {file = "google_api_python_client-2.161.0.tar.gz", hash = "sha256:324c0cce73e9ea0a0d2afd5937e01b7c2d6a4d7e2579cdb6c384f9699d6c9f37"},
+]
+
+[package.dependencies]
+google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0.dev0"
+google-auth = ">=1.32.0,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0.dev0"
+google-auth-httplib2 = ">=0.2.0,<1.0.0"
+httplib2 = ">=0.19.0,<1.dev0"
+uritemplate = ">=3.0.1,<5"
+
+[[package]]
+name = "google-auth"
+version = "2.38.0"
+description = "Google Authentication Library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"},
+ {file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"},
+]
+
+[package.dependencies]
+cachetools = ">=2.0.0,<6.0"
+pyasn1-modules = ">=0.2.1"
+rsa = ">=3.1.4,<5"
+
+[package.extras]
+aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"]
+enterprise-cert = ["cryptography", "pyopenssl"]
+pyjwt = ["cryptography (>=38.0.3)", "pyjwt (>=2.0)"]
+pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
+reauth = ["pyu2f (>=0.1.5)"]
+requests = ["requests (>=2.20.0,<3.0.0.dev0)"]
+
+[[package]]
+name = "google-auth-httplib2"
+version = "0.2.0"
+description = "Google Authentication Library: httplib2 transport"
+optional = false
+python-versions = "*"
+files = [
+ {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"},
+ {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"},
+]
+
+[package.dependencies]
+google-auth = "*"
+httplib2 = ">=0.19.0"
+
+[[package]]
+name = "google-cloud-core"
+version = "2.4.1"
+description = "Google Cloud API client core library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073"},
+ {file = "google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61"},
+]
+
+[package.dependencies]
+google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev"
+google-auth = ">=1.25.0,<3.0dev"
+
+[package.extras]
+grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"]
+
+[[package]]
+name = "google-cloud-firestore"
+version = "2.20.0"
+description = "Google Cloud Firestore API client library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "google_cloud_firestore-2.20.0-py2.py3-none-any.whl", hash = "sha256:505ef6951b56ea91f6762d99b3429ca6def6dceb254b4ba1ffc85e69b71414d7"},
+ {file = "google_cloud_firestore-2.20.0.tar.gz", hash = "sha256:ad863be58667233921e02f0b81aa94a816f5e63a707cb33c97b1aa357a50566d"},
+]
+
+[package.dependencies]
+google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]}
+google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev"
+google-cloud-core = ">=1.4.1,<3.0.0dev"
+proto-plus = [
+ {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""},
+ {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""},
+]
+protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev"
+
+[[package]]
+name = "google-cloud-storage"
+version = "3.0.0"
+description = "Google Cloud Storage API client library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "google_cloud_storage-3.0.0-py2.py3-none-any.whl", hash = "sha256:f85fd059650d2dbb0ac158a9a6b304b66143b35ed2419afec2905ca522eb2c6a"},
+ {file = "google_cloud_storage-3.0.0.tar.gz", hash = "sha256:2accb3e828e584888beff1165e5f3ac61aa9088965eb0165794a82d8c7f95297"},
+]
+
+[package.dependencies]
+google-api-core = ">=2.15.0,<3.0.0dev"
+google-auth = ">=2.26.1,<3.0dev"
+google-cloud-core = ">=2.3.0,<3.0dev"
+google-crc32c = ">=1.0,<2.0dev"
+google-resumable-media = ">=2.7.2"
+requests = ">=2.18.0,<3.0.0dev"
+
+[package.extras]
+protobuf = ["protobuf (<6.0.0dev)"]
+tracing = ["opentelemetry-api (>=1.1.0)"]
+
+[[package]]
+name = "google-crc32c"
+version = "1.6.0"
+description = "A python wrapper of the C library 'Google CRC32C'"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa"},
+ {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9"},
+ {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7"},
+ {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e"},
+ {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc"},
+ {file = "google_crc32c-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42"},
+ {file = "google_crc32c-1.6.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4"},
+ {file = "google_crc32c-1.6.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8"},
+ {file = "google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d"},
+ {file = "google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f"},
+ {file = "google_crc32c-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3"},
+ {file = "google_crc32c-1.6.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d"},
+ {file = "google_crc32c-1.6.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b"},
+ {file = "google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00"},
+ {file = "google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3"},
+ {file = "google_crc32c-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760"},
+ {file = "google_crc32c-1.6.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205"},
+ {file = "google_crc32c-1.6.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0"},
+ {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2"},
+ {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871"},
+ {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57"},
+ {file = "google_crc32c-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c"},
+ {file = "google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc"},
+ {file = "google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d"},
+ {file = "google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24"},
+ {file = "google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d"},
+ {file = "google_crc32c-1.6.0.tar.gz", hash = "sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc"},
+]
+
+[package.extras]
+testing = ["pytest"]
+
+[[package]]
+name = "google-resumable-media"
+version = "2.7.2"
+description = "Utilities for Google Media Downloads and Resumable Uploads"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa"},
+ {file = "google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0"},
+]
+
+[package.dependencies]
+google-crc32c = ">=1.0,<2.0dev"
+
+[package.extras]
+aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "google-auth (>=1.22.0,<2.0dev)"]
+requests = ["requests (>=2.18.0,<3.0.0dev)"]
+
+[[package]]
+name = "googleapis-common-protos"
+version = "1.67.0"
+description = "Common protobufs used in Google APIs"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "googleapis_common_protos-1.67.0-py2.py3-none-any.whl", hash = "sha256:579de760800d13616f51cf8be00c876f00a9f146d3e6510e19d1f4111758b741"},
+ {file = "googleapis_common_protos-1.67.0.tar.gz", hash = "sha256:21398025365f138be356d5923e9168737d94d46a72aefee4a6110a1f23463c86"},
+]
+
+[package.dependencies]
+protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0"
+
+[package.extras]
+grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"]
+
[[package]]
name = "greenlet"
version = "3.1.1"
@@ -516,6 +794,103 @@ files = [
docs = ["Sphinx", "furo"]
test = ["objgraph", "psutil"]
+[[package]]
+name = "grpcio"
+version = "1.70.0"
+description = "HTTP/2-based RPC framework"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "grpcio-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:95469d1977429f45fe7df441f586521361e235982a0b39e33841549143ae2851"},
+ {file = "grpcio-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ed9718f17fbdb472e33b869c77a16d0b55e166b100ec57b016dc7de9c8d236bf"},
+ {file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:374d014f29f9dfdb40510b041792e0e2828a1389281eb590df066e1cc2b404e5"},
+ {file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2af68a6f5c8f78d56c145161544ad0febbd7479524a59c16b3e25053f39c87f"},
+ {file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7df14b2dcd1102a2ec32f621cc9fab6695effef516efbc6b063ad749867295"},
+ {file = "grpcio-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c78b339869f4dbf89881e0b6fbf376313e4f845a42840a7bdf42ee6caed4b11f"},
+ {file = "grpcio-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58ad9ba575b39edef71f4798fdb5c7b6d02ad36d47949cd381d4392a5c9cbcd3"},
+ {file = "grpcio-1.70.0-cp310-cp310-win32.whl", hash = "sha256:2b0d02e4b25a5c1f9b6c7745d4fa06efc9fd6a611af0fb38d3ba956786b95199"},
+ {file = "grpcio-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:0de706c0a5bb9d841e353f6343a9defc9fc35ec61d6eb6111802f3aa9fef29e1"},
+ {file = "grpcio-1.70.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:17325b0be0c068f35770f944124e8839ea3185d6d54862800fc28cc2ffad205a"},
+ {file = "grpcio-1.70.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:dbe41ad140df911e796d4463168e33ef80a24f5d21ef4d1e310553fcd2c4a386"},
+ {file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5ea67c72101d687d44d9c56068328da39c9ccba634cabb336075fae2eab0d04b"},
+ {file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb5277db254ab7586769e490b7b22f4ddab3876c490da0a1a9d7c695ccf0bf77"},
+ {file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7831a0fc1beeeb7759f737f5acd9fdcda520e955049512d68fda03d91186eea"},
+ {file = "grpcio-1.70.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:27cc75e22c5dba1fbaf5a66c778e36ca9b8ce850bf58a9db887754593080d839"},
+ {file = "grpcio-1.70.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d63764963412e22f0491d0d32833d71087288f4e24cbcddbae82476bfa1d81fd"},
+ {file = "grpcio-1.70.0-cp311-cp311-win32.whl", hash = "sha256:bb491125103c800ec209d84c9b51f1c60ea456038e4734688004f377cfacc113"},
+ {file = "grpcio-1.70.0-cp311-cp311-win_amd64.whl", hash = "sha256:d24035d49e026353eb042bf7b058fb831db3e06d52bee75c5f2f3ab453e71aca"},
+ {file = "grpcio-1.70.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:ef4c14508299b1406c32bdbb9fb7b47612ab979b04cf2b27686ea31882387cff"},
+ {file = "grpcio-1.70.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:aa47688a65643afd8b166928a1da6247d3f46a2784d301e48ca1cc394d2ffb40"},
+ {file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:880bfb43b1bb8905701b926274eafce5c70a105bc6b99e25f62e98ad59cb278e"},
+ {file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e654c4b17d07eab259d392e12b149c3a134ec52b11ecdc6a515b39aceeec898"},
+ {file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2394e3381071045a706ee2eeb6e08962dd87e8999b90ac15c55f56fa5a8c9597"},
+ {file = "grpcio-1.70.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b3c76701428d2df01964bc6479422f20e62fcbc0a37d82ebd58050b86926ef8c"},
+ {file = "grpcio-1.70.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac073fe1c4cd856ebcf49e9ed6240f4f84d7a4e6ee95baa5d66ea05d3dd0df7f"},
+ {file = "grpcio-1.70.0-cp312-cp312-win32.whl", hash = "sha256:cd24d2d9d380fbbee7a5ac86afe9787813f285e684b0271599f95a51bce33528"},
+ {file = "grpcio-1.70.0-cp312-cp312-win_amd64.whl", hash = "sha256:0495c86a55a04a874c7627fd33e5beaee771917d92c0e6d9d797628ac40e7655"},
+ {file = "grpcio-1.70.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa573896aeb7d7ce10b1fa425ba263e8dddd83d71530d1322fd3a16f31257b4a"},
+ {file = "grpcio-1.70.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:d405b005018fd516c9ac529f4b4122342f60ec1cee181788249372524e6db429"},
+ {file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f32090238b720eb585248654db8e3afc87b48d26ac423c8dde8334a232ff53c9"},
+ {file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa089a734f24ee5f6880c83d043e4f46bf812fcea5181dcb3a572db1e79e01c"},
+ {file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19375f0300b96c0117aca118d400e76fede6db6e91f3c34b7b035822e06c35f"},
+ {file = "grpcio-1.70.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7c73c42102e4a5ec76608d9b60227d917cea46dff4d11d372f64cbeb56d259d0"},
+ {file = "grpcio-1.70.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0a5c78d5198a1f0aa60006cd6eb1c912b4a1520b6a3968e677dbcba215fabb40"},
+ {file = "grpcio-1.70.0-cp313-cp313-win32.whl", hash = "sha256:fe9dbd916df3b60e865258a8c72ac98f3ac9e2a9542dcb72b7a34d236242a5ce"},
+ {file = "grpcio-1.70.0-cp313-cp313-win_amd64.whl", hash = "sha256:4119fed8abb7ff6c32e3d2255301e59c316c22d31ab812b3fbcbaf3d0d87cc68"},
+ {file = "grpcio-1.70.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:8058667a755f97407fca257c844018b80004ae8035565ebc2812cc550110718d"},
+ {file = "grpcio-1.70.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:879a61bf52ff8ccacbedf534665bb5478ec8e86ad483e76fe4f729aaef867cab"},
+ {file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:0ba0a173f4feacf90ee618fbc1a27956bfd21260cd31ced9bc707ef551ff7dc7"},
+ {file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558c386ecb0148f4f99b1a65160f9d4b790ed3163e8610d11db47838d452512d"},
+ {file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:412faabcc787bbc826f51be261ae5fa996b21263de5368a55dc2cf824dc5090e"},
+ {file = "grpcio-1.70.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3b0f01f6ed9994d7a0b27eeddea43ceac1b7e6f3f9d86aeec0f0064b8cf50fdb"},
+ {file = "grpcio-1.70.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7385b1cb064734005204bc8994eed7dcb801ed6c2eda283f613ad8c6c75cf873"},
+ {file = "grpcio-1.70.0-cp38-cp38-win32.whl", hash = "sha256:07269ff4940f6fb6710951116a04cd70284da86d0a4368fd5a3b552744511f5a"},
+ {file = "grpcio-1.70.0-cp38-cp38-win_amd64.whl", hash = "sha256:aba19419aef9b254e15011b230a180e26e0f6864c90406fdbc255f01d83bc83c"},
+ {file = "grpcio-1.70.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:4f1937f47c77392ccd555728f564a49128b6a197a05a5cd527b796d36f3387d0"},
+ {file = "grpcio-1.70.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:0cd430b9215a15c10b0e7d78f51e8a39d6cf2ea819fd635a7214fae600b1da27"},
+ {file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e27585831aa6b57b9250abaf147003e126cd3a6c6ca0c531a01996f31709bed1"},
+ {file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1af8e15b0f0fe0eac75195992a63df17579553b0c4af9f8362cc7cc99ccddf4"},
+ {file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbce24409beaee911c574a3d75d12ffb8c3e3dd1b813321b1d7a96bbcac46bf4"},
+ {file = "grpcio-1.70.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ff4a8112a79464919bb21c18e956c54add43ec9a4850e3949da54f61c241a4a6"},
+ {file = "grpcio-1.70.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5413549fdf0b14046c545e19cfc4eb1e37e9e1ebba0ca390a8d4e9963cab44d2"},
+ {file = "grpcio-1.70.0-cp39-cp39-win32.whl", hash = "sha256:b745d2c41b27650095e81dea7091668c040457483c9bdb5d0d9de8f8eb25e59f"},
+ {file = "grpcio-1.70.0-cp39-cp39-win_amd64.whl", hash = "sha256:a31d7e3b529c94e930a117b2175b2efd179d96eb3c7a21ccb0289a8ab05b645c"},
+ {file = "grpcio-1.70.0.tar.gz", hash = "sha256:8d1584a68d5922330025881e63a6c1b54cc8117291d382e4fa69339b6d914c56"},
+]
+
+[package.extras]
+protobuf = ["grpcio-tools (>=1.70.0)"]
+
+[[package]]
+name = "grpcio-status"
+version = "1.70.0"
+description = "Status proto mapping for gRPC"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "grpcio_status-1.70.0-py3-none-any.whl", hash = "sha256:fc5a2ae2b9b1c1969cc49f3262676e6854aa2398ec69cb5bd6c47cd501904a85"},
+ {file = "grpcio_status-1.70.0.tar.gz", hash = "sha256:0e7b42816512433b18b9d764285ff029bde059e9d41f8fe10a60631bd8348101"},
+]
+
+[package.dependencies]
+googleapis-common-protos = ">=1.5.5"
+grpcio = ">=1.70.0"
+protobuf = ">=5.26.1,<6.0dev"
+
+[[package]]
+name = "httplib2"
+version = "0.22.0"
+description = "A comprehensive HTTP client library."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"},
+ {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"},
+]
+
+[package.dependencies]
+pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""}
+
[[package]]
name = "icalendar"
version = "6.1.0"
@@ -814,6 +1189,79 @@ files = [
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
]
+[[package]]
+name = "msgpack"
+version = "1.1.0"
+description = "MessagePack serializer"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"},
+ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"},
+ {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"},
+ {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"},
+ {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"},
+ {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"},
+ {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"},
+ {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"},
+ {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"},
+ {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"},
+ {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"},
+ {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"},
+ {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"},
+ {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"},
+ {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"},
+ {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"},
+ {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"},
+ {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"},
+ {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"},
+ {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"},
+ {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"},
+ {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"},
+ {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"},
+ {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"},
+ {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"},
+ {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"},
+ {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"},
+ {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"},
+ {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"},
+ {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"},
+ {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"},
+ {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"},
+ {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"},
+ {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"},
+ {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"},
+ {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"},
+ {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"},
+ {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"},
+ {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"},
+ {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"},
+ {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"},
+ {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"},
+ {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"},
+ {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"},
+ {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"},
+ {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"},
+ {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"},
+ {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"},
+ {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"},
+ {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"},
+ {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"},
+ {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"},
+ {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"},
+ {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"},
+ {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"},
+ {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"},
+ {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"},
+ {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"},
+ {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"},
+ {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"},
+ {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"},
+ {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"},
+ {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"},
+ {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"},
+]
+
[[package]]
name = "mypy"
version = "1.14.0"
@@ -1037,6 +1485,43 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
typing = ["typing-extensions"]
xmp = ["defusedxml"]
+[[package]]
+name = "proto-plus"
+version = "1.26.0"
+description = "Beautiful, Pythonic protocol buffers"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "proto_plus-1.26.0-py3-none-any.whl", hash = "sha256:bf2dfaa3da281fc3187d12d224c707cb57214fb2c22ba854eb0c105a3fb2d4d7"},
+ {file = "proto_plus-1.26.0.tar.gz", hash = "sha256:6e93d5f5ca267b54300880fff156b6a3386b3fa3f43b1da62e680fc0c586ef22"},
+]
+
+[package.dependencies]
+protobuf = ">=3.19.0,<6.0.0dev"
+
+[package.extras]
+testing = ["google-api-core (>=1.31.5)"]
+
+[[package]]
+name = "protobuf"
+version = "5.29.3"
+description = ""
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"},
+ {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"},
+ {file = "protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e"},
+ {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84"},
+ {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f"},
+ {file = "protobuf-5.29.3-cp38-cp38-win32.whl", hash = "sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252"},
+ {file = "protobuf-5.29.3-cp38-cp38-win_amd64.whl", hash = "sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107"},
+ {file = "protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7"},
+ {file = "protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da"},
+ {file = "protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f"},
+ {file = "protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620"},
+]
+
[[package]]
name = "psycopg2"
version = "2.9.10"
@@ -1050,6 +1535,7 @@ files = [
{file = "psycopg2-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4"},
{file = "psycopg2-2.9.10-cp312-cp312-win32.whl", hash = "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067"},
{file = "psycopg2-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e"},
+ {file = "psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2"},
{file = "psycopg2-2.9.10-cp39-cp39-win32.whl", hash = "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b"},
{file = "psycopg2-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442"},
{file = "psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11"},
@@ -1069,6 +1555,31 @@ files = [
[package.extras]
tests = ["pytest"]
+[[package]]
+name = "pyasn1"
+version = "0.6.1"
+description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
+ {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
+]
+
+[[package]]
+name = "pyasn1-modules"
+version = "0.4.1"
+description = "A collection of ASN.1-based protocols modules"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"},
+ {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"},
+]
+
+[package.dependencies]
+pyasn1 = ">=0.4.6,<0.7.0"
+
[[package]]
name = "pycparser"
version = "2.22"
@@ -1080,6 +1591,26 @@ files = [
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
+[[package]]
+name = "pyjwt"
+version = "2.10.1"
+description = "JSON Web Token implementation in Python"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"},
+ {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"},
+]
+
+[package.dependencies]
+cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""}
+
+[package.extras]
+crypto = ["cryptography (>=3.4.0)"]
+dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
+docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
+tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
+
[[package]]
name = "pymysql"
version = "1.1.1"
@@ -1095,6 +1626,20 @@ files = [
ed25519 = ["PyNaCl (>=1.4.0)"]
rsa = ["cryptography"]
+[[package]]
+name = "pyparsing"
+version = "3.2.1"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"},
+ {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"},
+]
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
[[package]]
name = "pypdfium2"
version = "4.30.1"
@@ -1214,6 +1759,20 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+[[package]]
+name = "rsa"
+version = "4.9"
+description = "Pure-Python RSA implementation"
+optional = false
+python-versions = ">=3.6,<4"
+files = [
+ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
+ {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
+]
+
+[package.dependencies]
+pyasn1 = ">=0.1.3"
+
[[package]]
name = "ruff"
version = "0.8.4"
@@ -1391,7 +1950,7 @@ files = [
]
[package.dependencies]
-greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
+greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"}
typing-extensions = ">=4.6.0"
[package.extras]
@@ -1543,6 +2102,17 @@ files = [
{file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
]
+[[package]]
+name = "uritemplate"
+version = "4.1.1"
+description = "Implementation of RFC 6570 URI Templates"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"},
+ {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"},
+]
+
[[package]]
name = "urllib3"
version = "2.3.0"
@@ -1586,4 +2156,4 @@ sentry = ["sentry-sdk"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
-content-hash = "5053549181384bee0a3d5189fa1de2f9449fc8eb502cf6ce84131b98d1d3ebc6"
+content-hash = "404062eeabcfb7d60e874e215cdac57d743314eddc87cd85fba49e5831add2b8"
diff --git a/API/pyproject.toml b/API/pyproject.toml
index 6ecab76..1cc4b2d 100644
--- a/API/pyproject.toml
+++ b/API/pyproject.toml
@@ -49,6 +49,7 @@ pdfplumber = "^0.11.4"
openpyxl = "^3.1.5"
mammoth = "^1.8.0"
icalendar = "^6.1.0"
+firebase-admin = "^6.6.0"
beautifulsoup4 = { version = "^4.12.3", extras = ["lxml"] }
# Optional support for Sentry integration
diff --git a/website/package.json b/website/package.json
index bff2ddd..551eeeb 100644
--- a/website/package.json
+++ b/website/package.json
@@ -30,11 +30,13 @@
"@sentry/core": "^8.47.0",
"@sentry/vue": "^8.47.0",
"@vueuse/core": "^12.2.0",
+ "firebase": "^11.1.0",
"pinia": "^2.3.0",
"pinia-plugin-persistedstate": "^4.2.0",
"pulltorefreshjs": "^0.1.22",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
+ "vuefire": "^3.2.1",
"vuetify": "^3.7.6"
},
"devDependencies": {
diff --git a/website/src/App.vue b/website/src/App.vue
index 5db4f12..04d6131 100644
--- a/website/src/App.vue
+++ b/website/src/App.vue
@@ -1,10 +1,10 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/src/components/NotificationsSetTime.vue b/website/src/components/NotificationsSetTime.vue
new file mode 100644
index 0000000..390cdaa
--- /dev/null
+++ b/website/src/components/NotificationsSetTime.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/src/components/SettingsBaseAction.vue b/website/src/components/SettingsBaseAction.vue
index 3a929a8..9593ff7 100644
--- a/website/src/components/SettingsBaseAction.vue
+++ b/website/src/components/SettingsBaseAction.vue
@@ -2,9 +2,10 @@
const model = defineModel()
const { callback } = defineProps<{
- icon: string
- messages?: string
label: string
+ messages?: string
+ icon: string
+ disabled?: boolean
callback?: () => void
}>()
@@ -18,6 +19,7 @@ function doAction() {
.v-input__control {
+ opacity: var(--v-disabled-opacity);
+}
diff --git a/website/src/components/SettingsBaseSwitch.vue b/website/src/components/SettingsBaseSwitch.vue
index cb8bdd2..d656cf0 100644
--- a/website/src/components/SettingsBaseSwitch.vue
+++ b/website/src/components/SettingsBaseSwitch.vue
@@ -1,14 +1,20 @@
-
- {{ label }}
-
+
diff --git a/website/src/components/SettingsSelectEntity.vue b/website/src/components/SettingsSelectEntity.vue
index 70e4d38..bf71e62 100644
--- a/website/src/components/SettingsSelectEntity.vue
+++ b/website/src/components/SettingsSelectEntity.vue
@@ -6,6 +6,7 @@ import { useRouter } from 'vue-router'
import { useSnackbarStore } from '@/composables/snackbar'
import { useListsStore } from '@/stores/lists'
+import { useNotificationsStore } from '@/stores/notifications'
import { EntityType, useSettingsStore } from '@/stores/settings'
import { sortEntities } from '@/utils/entities'
import { localizeSelectEntityNotSelected, localizeSelectEntityTitle } from '@/utils/localization'
@@ -16,6 +17,8 @@ const { fullPage } = defineProps<{ fullPage?: boolean }>()
const router = useRouter()
+const notificationsStore = useNotificationsStore()
+
const settingsStore = useSettingsStore()
const { displaySnackbar } = useSnackbarStore()
@@ -197,6 +200,8 @@ function handleSaveEntityList() {
// Navigate to the correct timetable route or close the dialog
if (fullPage) router.push(generateTimetableRoute(selectedType.value, selectedList.value))
else displayed.value = false
+
+ notificationsStore.updateUserFirestoreData()
}
diff --git a/website/src/components/SettingsSelectMenuLunch.vue b/website/src/components/SettingsSelectMenuLunch.vue
index ccd8583..db5413b 100644
--- a/website/src/components/SettingsSelectMenuLunch.vue
+++ b/website/src/components/SettingsSelectMenuLunch.vue
@@ -1,12 +1,18 @@
diff --git a/website/src/components/SettingsSelectMenuSnack.vue b/website/src/components/SettingsSelectMenuSnack.vue
index d95c116..2f91393 100644
--- a/website/src/components/SettingsSelectMenuSnack.vue
+++ b/website/src/components/SettingsSelectMenuSnack.vue
@@ -1,12 +1,18 @@
diff --git a/website/src/main.ts b/website/src/main.ts
index 747cb5c..827c40b 100644
--- a/website/src/main.ts
+++ b/website/src/main.ts
@@ -1,8 +1,10 @@
import './assets/main.css'
import { createApp } from 'vue'
+import { VueFire } from 'vuefire'
import App from './App.vue'
+import firebaseApp from './plugins/firebase'
import pinia from './plugins/pinia'
import vuetify from './plugins/vuetify'
import registerSentry from './registerSentry'
@@ -14,6 +16,7 @@ const app = createApp(App)
app.use(pinia)
app.use(router)
app.use(vuetify)
+app.use(VueFire, { firebaseApp })
registerSentry(app, router)
registerServiceWorker(router)
diff --git a/website/src/plugins/firebase.ts b/website/src/plugins/firebase.ts
new file mode 100644
index 0000000..e86a3aa
--- /dev/null
+++ b/website/src/plugins/firebase.ts
@@ -0,0 +1,12 @@
+import { initializeApp } from 'firebase/app'
+
+const firebaseApp = initializeApp({
+ apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
+ authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
+ projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
+ storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
+ messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
+ appId: import.meta.env.VITE_FIREBASE_APP_ID,
+})
+
+export default firebaseApp
diff --git a/website/src/plugins/vuetify.ts b/website/src/plugins/vuetify.ts
index 501d320..2e3640a 100644
--- a/website/src/plugins/vuetify.ts
+++ b/website/src/plugins/vuetify.ts
@@ -4,6 +4,7 @@ import type { ThemeDefinition } from 'vuetify'
import { createVuetify } from 'vuetify'
import { VBtn, VCard, VDivider, VSheet, VTable } from 'vuetify/components'
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
+import { VTimePicker } from 'vuetify/labs/VTimePicker'
import { sl } from 'vuetify/locale'
import { AccentColorName, accentColors } from '@/utils/colors'
@@ -72,6 +73,10 @@ const vuetify = createVuetify({
messages: { sl },
},
+ components: {
+ VTimePicker,
+ },
+
aliases: {
VDividerSettings: VDivider,
VBtnIcon: VBtn,
diff --git a/website/src/registerSentry.ts b/website/src/registerSentry.ts
index 22f2f82..883a0f2 100644
--- a/website/src/registerSentry.ts
+++ b/website/src/registerSentry.ts
@@ -54,7 +54,7 @@ export default function registerSentry(app: App, router: Router) {
'ViewMenu',
'ViewCirculars',
'ViewSources',
- 'ViewSubscribe',
+ 'ViewNotifications',
'ViewSettings',
'ViewWelcome',
'NotFound',
diff --git a/website/src/router/index.ts b/website/src/router/index.ts
index 2fa6312..06843ca 100644
--- a/website/src/router/index.ts
+++ b/website/src/router/index.ts
@@ -2,13 +2,14 @@ import { nextTick } from 'vue'
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { homeGuard, timetableGuard, welcomeGuard } from '@/router/guards'
+import { useNotificationsStore } from '@/stores/notifications'
import { useSessionStore } from '@/stores/session'
const Timetable = () => import('../views/ViewTimetable.vue')
const Menu = () => import('../views/ViewMenu.vue')
const Circulars = () => import('../views/ViewCirculars.vue')
const Sources = () => import('../views/ViewSources.vue')
-const Subscribe = () => import('../views/ViewSubscribe.vue')
+const Notifications = () => import('../views/ViewNotifications.vue')
const Settings = () => import('../views/ViewSettings.vue')
const Welcome = () => import('../views/ViewWelcome.vue')
const NotFound = () => import('../views/NotFound.vue')
@@ -22,7 +23,7 @@ const routes: RouteRecordRaw[] = [
{ path: '/menu', name: 'menu', component: Menu, meta: { title: 'Jedilnik', allowPullToRefresh: true, showDayTabs: true } },
{ path: '/circulars', name: 'circulars', component: Circulars, meta: { title: 'Okrožnice', allowPullToRefresh: true } },
{ path: '/sources', name: 'sources', component: Sources, meta: { title: 'Viri', allowPullToRefresh: true } },
- { path: '/subscribe', name: 'subscribe', component: Subscribe, meta: { title: 'Naročanje' } },
+ { path: '/notifications', name: 'notifications', component: Notifications, meta: { title: 'Sporočila', allowPullToRefresh: true } },
{ path: '/settings', name: 'settings', component: Settings, meta: { title: 'Nastavitve' } },
{ path: '/:pathMatch(.*)*', name: 'notFound', component: NotFound, meta: { title: 'Stran ni najdena' } },
]
@@ -42,6 +43,10 @@ router.beforeEach((to, from) => {
// Included just in case the route priority changes and welcome is matched first
if (to.name === 'welcome') return welcomeGuard()
+ // Check the notifications
+ const notificationsStore = useNotificationsStore()
+ notificationsStore.updateNotifications()
+
// Call timetable guard that redirects the user to the correct entity
if (to.name === 'timetable') return timetableGuard(to, from)
})
diff --git a/website/src/stores/notifications.ts b/website/src/stores/notifications.ts
new file mode 100644
index 0000000..b152b6b
--- /dev/null
+++ b/website/src/stores/notifications.ts
@@ -0,0 +1,112 @@
+import { doc, setDoc } from 'firebase/firestore'
+import { getMessaging, getToken } from 'firebase/messaging'
+import { defineStore } from 'pinia'
+import { useFirestore } from 'vuefire'
+
+import { useSnackbarStore } from '@/composables/snackbar'
+import firebaseApp from '@/plugins/firebase'
+import router from '@/router'
+import { useSettingsStore } from '@/stores/settings'
+import { updateWrapper } from '@/utils/update'
+
+export interface Notification {
+ date: string
+ title: string
+ content: string | null
+}
+
+export const useNotificationsStore = defineStore('notifications', {
+ state: () => ({
+ token: '',
+
+ immediateSubstitutionsNotificationsEnabled: false,
+ scheduledSubstitutionsNotificationsEnabled: false,
+ scheduledSubstitutionsNotificationsCurrentDayTime: '',
+ scheduledSubstitutionsNotificationsNextDayTime: '20:00',
+
+ circularsNotificationsEnabled: false,
+
+ snackMenuNotificationsEnabled: false,
+ lunchMenuNotificationsEnabled: false,
+
+ notifications: [] as Notification[],
+
+ seen: true,
+ }),
+
+ actions: {
+ getFCMToken() {
+ const messaging = getMessaging(firebaseApp)
+ getToken(messaging, { vapidKey: import.meta.env.VITE_FIREBASE_VAPID_KEY }).then(
+ currentToken => {
+ if (currentToken) {
+ this.token = currentToken
+ this.updateUserFirestoreData()
+ } else {
+ console.log('Could not get FCM token')
+ }
+ },
+ )
+ },
+
+ async updateUserFirestoreData() {
+ if (Notification.permission !== 'granted') return
+
+ if (!this.token) {
+ this.getFCMToken()
+ return
+ }
+
+ const { entityType, entityList } = useSettingsStore()
+
+ const db = useFirestore()
+
+ await setDoc(doc(db, 'users', this.token), {
+ immediateSubstitutionsNotificationsEnabled: this.immediateSubstitutionsNotificationsEnabled,
+ scheduledSubstitutionsNotificationsEnabled: this.scheduledSubstitutionsNotificationsEnabled,
+ scheduledSubstitutionsNotificationsCurrentDayTime:
+ this.scheduledSubstitutionsNotificationsCurrentDayTime,
+ scheduledSubstitutionsNotificationsNextDayTime:
+ this.scheduledSubstitutionsNotificationsNextDayTime,
+
+ circularsNotificationsEnabled: this.circularsNotificationsEnabled,
+
+ snackMenuNotificationsEnabled: this.snackMenuNotificationsEnabled,
+ lunchMenuNotificationsEnabled: this.lunchMenuNotificationsEnabled,
+
+ entityType: entityType,
+ entityList: entityList,
+ })
+ },
+
+ async updateNotifications() {
+ await updateWrapper(async () => {
+ const response = await fetch(import.meta.env.VITE_API + '/notifications')
+ const notifications = await response.json()
+
+ const latestNotificationDate = new Date(notifications[notifications.length - 1].date)
+
+ // Display the snackbar if new notifications were sent and are not older than 14 days
+ if (
+ notifications.length > this.notifications.length &&
+ latestNotificationDate >= new Date(Date.now() - 14 * 24 * 60 * 60 * 1000)
+ ) {
+ const snackbarStore = useSnackbarStore()
+
+ snackbarStore.displaySnackbar(
+ 'Novo obvestilo uporabnikom',
+ 'Oglej',
+ () => router.push({ path: '/notifications' }),
+ -1,
+ )
+
+ this.seen = false
+ }
+
+ this.notifications = notifications.reverse()
+ })
+ },
+ },
+
+ persist: true,
+})
diff --git a/website/src/utils/update.ts b/website/src/utils/update.ts
index 8a42e86..2e83bbc 100644
--- a/website/src/utils/update.ts
+++ b/website/src/utils/update.ts
@@ -5,6 +5,7 @@ import { useSnackbarStore } from '@/composables/snackbar'
import { useDocumentsStore } from '@/stores/documents'
import { useFoodStore } from '@/stores/food'
import { useListsStore } from '@/stores/lists'
+import { useNotificationsStore } from '@/stores/notifications'
import { useSettingsStore } from '@/stores/settings'
import { useTimetableStore } from '@/stores/timetable'
@@ -13,6 +14,7 @@ export async function updateAllData(showSuccess: boolean = true): Promise
const foodStore = useFoodStore()
const timetableStore = useTimetableStore()
const listsStore = useListsStore()
+ const notificationsStore = useNotificationsStore()
const { displaySnackbar } = useSnackbarStore()
@@ -33,6 +35,7 @@ export async function updateAllData(showSuccess: boolean = true): Promise
timetableStore.updateSubstitutions(),
timetableStore.updateEmptyClassrooms(),
listsStore.updateLists(),
+ notificationsStore.updateNotifications(),
])
if (showSuccess) {
diff --git a/website/src/views/ViewNotifications.vue b/website/src/views/ViewNotifications.vue
new file mode 100644
index 0000000..23bf0e1
--- /dev/null
+++ b/website/src/views/ViewNotifications.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/src/views/ViewSources.vue b/website/src/views/ViewSources.vue
index b755e00..b0bcca4 100644
--- a/website/src/views/ViewSources.vue
+++ b/website/src/views/ViewSources.vue
@@ -1,16 +1,24 @@
@@ -18,5 +26,25 @@ const menus = computed(() => filterDocuments(['snack-menu', 'lunch-menu']))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Koledarji
+
+
+
+
+
diff --git a/website/src/views/ViewSubscribe.vue b/website/src/views/ViewSubscribe.vue
deleted file mode 100644
index ae20862..0000000
--- a/website/src/views/ViewSubscribe.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
Viri
-
-
-
-
-
-
-
-
Koledarji
-
-
-
-
-
-
-
diff --git a/website/vite.config.ts b/website/vite.config.ts
index 496d956..80ca513 100644
--- a/website/vite.config.ts
+++ b/website/vite.config.ts
@@ -98,7 +98,7 @@ export default defineConfig(({ mode }) => {
'^/menu$': 'src/views/ViewMenu.vue',
'^/circulars$': 'src/views/ViewCirculars.vue',
'^/sources$': 'src/views/ViewSources.vue',
- '^/subscribe$': 'src/views/ViewSubscribe.vue',
+ '^/notifications$': 'src/views/ViewNotifications.vue',
'^/settings$': 'src/views/ViewSettings.vue',
}
diff --git a/website/yarn.lock b/website/yarn.lock
index 8675c4a..7af1367 100644
--- a/website/yarn.lock
+++ b/website/yarn.lock
@@ -1464,6 +1464,568 @@ __metadata:
languageName: node
linkType: hard
+"@firebase/analytics-compat@npm:0.2.16":
+ version: 0.2.16
+ resolution: "@firebase/analytics-compat@npm:0.2.16"
+ dependencies:
+ "@firebase/analytics": "npm:0.10.10"
+ "@firebase/analytics-types": "npm:0.8.3"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/c4a91732827cb16c91bb2f19a77d85c274a0a1de20fd2ae2fcb92a55d6fe5cd60fe66ef687f80467f75aeaa49c3a2c68d485616b98a5f5c7f3a3f7960eb9b2a6
+ languageName: node
+ linkType: hard
+
+"@firebase/analytics-types@npm:0.8.3":
+ version: 0.8.3
+ resolution: "@firebase/analytics-types@npm:0.8.3"
+ checksum: 10c0/2cbc5fe8425bc01c7ba03579cdc5ca6b23de51b08edb62927be610a33bbc961bae97aa48ee12dcdb039b752c158d095f234ed20f1f4d2bd7a5c39f44d82cdf22
+ languageName: node
+ linkType: hard
+
+"@firebase/analytics@npm:0.10.10":
+ version: 0.10.10
+ resolution: "@firebase/analytics@npm:0.10.10"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/installations": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/909f191e1ff8046088387a6fca901834fb0378b4e75314d27a605011559a9d06cd0bbb04826e552907ecce459d158c56c982e032b5383f1dabe8d8c906ce9f01
+ languageName: node
+ linkType: hard
+
+"@firebase/app-check-compat@npm:0.3.17":
+ version: 0.3.17
+ resolution: "@firebase/app-check-compat@npm:0.3.17"
+ dependencies:
+ "@firebase/app-check": "npm:0.8.10"
+ "@firebase/app-check-types": "npm:0.5.3"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/3e89e78d044c66c1d036f6f078eb27e6738c0a017cd1e71edb9e3b40efdd61fe36a2c6c4de755ff0cef15574f70c178c8874f95f709ace68013934f6c160a235
+ languageName: node
+ linkType: hard
+
+"@firebase/app-check-interop-types@npm:0.3.3":
+ version: 0.3.3
+ resolution: "@firebase/app-check-interop-types@npm:0.3.3"
+ checksum: 10c0/4a887ef5e30ee1a407b569603c433a9f21244d50a19d97a5f1f17d8f5caea83096852b39e67d599f3238f1f7e2a369b02d184a184986a649ed1f8fed12fbd6be
+ languageName: node
+ linkType: hard
+
+"@firebase/app-check-types@npm:0.5.3":
+ version: 0.5.3
+ resolution: "@firebase/app-check-types@npm:0.5.3"
+ checksum: 10c0/59af0ae698ff2172e84f504e3b5e778c2cc78fefdcceb917eb899a204ad130ad5497011ab94459f6f9dd0a9062a0455bbd745ad3e488b39dae4625c3fb0d0145
+ languageName: node
+ linkType: hard
+
+"@firebase/app-check@npm:0.8.10":
+ version: 0.8.10
+ resolution: "@firebase/app-check@npm:0.8.10"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/0e189362413b7592f13dcd0dc471cad2d94b3927272d6b0e839c7020c3427ae22d92f57246b49e88d2d952c6651cb1bd4c1c7fb0b9b5134eb7928dcc3aa02468
+ languageName: node
+ linkType: hard
+
+"@firebase/app-compat@npm:0.2.47":
+ version: 0.2.47
+ resolution: "@firebase/app-compat@npm:0.2.47"
+ dependencies:
+ "@firebase/app": "npm:0.10.17"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/e126992792c0cc1231336e8289e38b239920fe2db65fe2f895c1d9ef078a1b357b6f0a00dc9f94f6fe961ed59e916ce577b22f8ffeef508e2794348ffab1adaa
+ languageName: node
+ linkType: hard
+
+"@firebase/app-types@npm:0.9.3":
+ version: 0.9.3
+ resolution: "@firebase/app-types@npm:0.9.3"
+ checksum: 10c0/02ec9a26c10b9bbb2a1e5b9ae0552b5325b40066e3c23be089ceae53414a1505f2ab716ae1098652a0a0c9992ba322c05371a9b2a837cccfae309788372a72e0
+ languageName: node
+ linkType: hard
+
+"@firebase/app@npm:0.10.17":
+ version: 0.10.17
+ resolution: "@firebase/app@npm:0.10.17"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ idb: "npm:7.1.1"
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/942fad056b3f42e2a4943c85324cee86fc0be484da670cf032b25e09de81f449d253068362dac6078cac739b4126f936493664e9d40031fa08285785eda452e5
+ languageName: node
+ linkType: hard
+
+"@firebase/auth-compat@npm:0.5.16":
+ version: 0.5.16
+ resolution: "@firebase/auth-compat@npm:0.5.16"
+ dependencies:
+ "@firebase/auth": "npm:1.8.1"
+ "@firebase/auth-types": "npm:0.12.3"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/01ebf2251c5d995f2b7d87523875da10a813f76af4d13fef2fb4be3dcd407252e65f8c48af9c9b2d8cc0cdda8294de27674489e21b431f8b82a8b2c38d82067b
+ languageName: node
+ linkType: hard
+
+"@firebase/auth-interop-types@npm:0.2.4":
+ version: 0.2.4
+ resolution: "@firebase/auth-interop-types@npm:0.2.4"
+ checksum: 10c0/ff833bcbb472992c6061847309e338dac736c616522c5fd808526d6dc13b9788458a8c9677d91c33c1288ee38f42896c2b4b8fe10ee74f1569d11f3f3c4f53b5
+ languageName: node
+ linkType: hard
+
+"@firebase/auth-types@npm:0.12.3":
+ version: 0.12.3
+ resolution: "@firebase/auth-types@npm:0.12.3"
+ peerDependencies:
+ "@firebase/app-types": 0.x
+ "@firebase/util": 1.x
+ checksum: 10c0/8666c6b7dda15965ad0300c18c742eb10e5f3a49fa255e169fd8af2b5b2088e65db24f66eaa7889ef92626c6a3de0b7f1a05960c4e9645f4d1111601121cb148
+ languageName: node
+ linkType: hard
+
+"@firebase/auth@npm:1.8.1":
+ version: 1.8.1
+ resolution: "@firebase/auth@npm:1.8.1"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ "@react-native-async-storage/async-storage": ^1.18.1
+ peerDependenciesMeta:
+ "@react-native-async-storage/async-storage":
+ optional: true
+ checksum: 10c0/01755c08fda1fea7b50ba22a5cb0e3663be62c9096d0d48201e54ad5d96c4a24259b45117163a150a20cecdf606133b14b939cb5219c28b0c4bd4f003db978e4
+ languageName: node
+ linkType: hard
+
+"@firebase/component@npm:0.6.11":
+ version: 0.6.11
+ resolution: "@firebase/component@npm:0.6.11"
+ dependencies:
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/788d66a0acb506507042173d1906edaf533ca68405f84aed16f33d8f2a130a8796e2f5c2d80177fc6c1826b74ea510da4541df9c381f6bf0f2b5417d3527797c
+ languageName: node
+ linkType: hard
+
+"@firebase/data-connect@npm:0.1.3":
+ version: 0.1.3
+ resolution: "@firebase/data-connect@npm:0.1.3"
+ dependencies:
+ "@firebase/auth-interop-types": "npm:0.2.4"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/6919efd9506d6f0f69635765996b301f117675c4df514aefe20a2c9f4066f2fb5bdecad89a443eb94eacc464713422b32378862ab291b2bdf108c0cbc4d50154
+ languageName: node
+ linkType: hard
+
+"@firebase/database-compat@npm:2.0.1":
+ version: 2.0.1
+ resolution: "@firebase/database-compat@npm:2.0.1"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/database": "npm:1.0.10"
+ "@firebase/database-types": "npm:1.0.7"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/e63c3f432d49c0cebc7f36da97d497ece86fa7d1d68bc59020395f96a3e10a16acf299d6299127a4ef8b8abd5f08ea257c5de3e9af44640f4517021a21495a4f
+ languageName: node
+ linkType: hard
+
+"@firebase/database-types@npm:1.0.7":
+ version: 1.0.7
+ resolution: "@firebase/database-types@npm:1.0.7"
+ dependencies:
+ "@firebase/app-types": "npm:0.9.3"
+ "@firebase/util": "npm:1.10.2"
+ checksum: 10c0/12c1f6b489d662f1191b65c1cd08cea1c60591f24867241d8861cf5c21e0b6402f7af2e832e35bc43cdc94dd00658da0d124009d4b3ab036f188299fbb8561d8
+ languageName: node
+ linkType: hard
+
+"@firebase/database@npm:1.0.10":
+ version: 1.0.10
+ resolution: "@firebase/database@npm:1.0.10"
+ dependencies:
+ "@firebase/app-check-interop-types": "npm:0.3.3"
+ "@firebase/auth-interop-types": "npm:0.2.4"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ faye-websocket: "npm:0.11.4"
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/c159b14f91824ce37c59630ac8333befb63e223289a0fbed4f8a6551a39090dc9893a5a34b89034888d18fa80a1831567688273a07d08f4a101bb206a02daf9a
+ languageName: node
+ linkType: hard
+
+"@firebase/firestore-compat@npm:0.3.40":
+ version: 0.3.40
+ resolution: "@firebase/firestore-compat@npm:0.3.40"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/firestore": "npm:4.7.5"
+ "@firebase/firestore-types": "npm:3.0.3"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/3cdaf8789e860d7460d36265516bb5516d252307c0cb58b00272c51f856973c347c13e119c258c5b2eb39071409c16fa84f583b8396d39248668cec0a164991d
+ languageName: node
+ linkType: hard
+
+"@firebase/firestore-types@npm:3.0.3":
+ version: 3.0.3
+ resolution: "@firebase/firestore-types@npm:3.0.3"
+ peerDependencies:
+ "@firebase/app-types": 0.x
+ "@firebase/util": 1.x
+ checksum: 10c0/8196168a2de68bd60e0a9053a670d14d2917bf8e30829a4a2f8435fa2aceaaf97ce7438cd9525786a9bf8c5d6104ced3086acd792439371fea7b35497a53bdfa
+ languageName: node
+ linkType: hard
+
+"@firebase/firestore@npm:4.7.5":
+ version: 4.7.5
+ resolution: "@firebase/firestore@npm:4.7.5"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ "@firebase/webchannel-wrapper": "npm:1.0.3"
+ "@grpc/grpc-js": "npm:~1.9.0"
+ "@grpc/proto-loader": "npm:^0.7.8"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/e176609c492b39231514f31c1f68c42cc7093a781d05124c68cf6aaf29d82bf9129a0ac6d02325d5408d34a092368128bd7e073fe490f93c72c9f6c3bf4851aa
+ languageName: node
+ linkType: hard
+
+"@firebase/functions-compat@npm:0.3.17":
+ version: 0.3.17
+ resolution: "@firebase/functions-compat@npm:0.3.17"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/functions": "npm:0.12.0"
+ "@firebase/functions-types": "npm:0.6.3"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/dff84d87a34916396217de802630ee02273b3321957d8a90f56888e80fcbc725cf4ea2311e6bc8f5fb072ce2f9440b784f4879430ec18118f6c3c0c8b1d605a7
+ languageName: node
+ linkType: hard
+
+"@firebase/functions-types@npm:0.6.3":
+ version: 0.6.3
+ resolution: "@firebase/functions-types@npm:0.6.3"
+ checksum: 10c0/aabd7bdd8c479323a419bba9ad275d96cd44229bd2213c87be08a9978af5ff0c1306279229a358c77280ce54fa6f42c91a6fd6c947808b1103174db0261b86e1
+ languageName: node
+ linkType: hard
+
+"@firebase/functions@npm:0.12.0":
+ version: 0.12.0
+ resolution: "@firebase/functions@npm:0.12.0"
+ dependencies:
+ "@firebase/app-check-interop-types": "npm:0.3.3"
+ "@firebase/auth-interop-types": "npm:0.2.4"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/messaging-interop-types": "npm:0.2.3"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/ce0c204aa54b54e4dbe9fbc6ebaeb086a584edda5a46df4e4f38c919abc1e38c6d12d2af67b408e3618079b4164646646cb5158e279abef8ff56c39e35c8e28f
+ languageName: node
+ linkType: hard
+
+"@firebase/installations-compat@npm:0.2.11":
+ version: 0.2.11
+ resolution: "@firebase/installations-compat@npm:0.2.11"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/installations": "npm:0.6.11"
+ "@firebase/installations-types": "npm:0.5.3"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/3cab30c2c9c8db37e34d9b6b79022145fe2ae5ad71edff6ca880e6dbe020bb06c03757f5a69642b73c25242d2d1b92d14f65f8a2ab10b6f29c20602fff7faa4e
+ languageName: node
+ linkType: hard
+
+"@firebase/installations-types@npm:0.5.3":
+ version: 0.5.3
+ resolution: "@firebase/installations-types@npm:0.5.3"
+ peerDependencies:
+ "@firebase/app-types": 0.x
+ checksum: 10c0/f8af07a17e9c0cd1738009b880579b57d112f991ac99e4a17f327d89ad9f8f633fd50757bfd97f470edcc62045526dc59432fb7fcb1f76daa3c72c975519af62
+ languageName: node
+ linkType: hard
+
+"@firebase/installations@npm:0.6.11":
+ version: 0.6.11
+ resolution: "@firebase/installations@npm:0.6.11"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/util": "npm:1.10.2"
+ idb: "npm:7.1.1"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/4af7d5d7d9c4a0792a3ecfef510410b426beec085ae8cc6ae71b79fec47c68976239744c004d0239f5c759005f32cd5fb35d80c0f4725d8b47b4970ec5745ce0
+ languageName: node
+ linkType: hard
+
+"@firebase/logger@npm:0.4.4":
+ version: 0.4.4
+ resolution: "@firebase/logger@npm:0.4.4"
+ dependencies:
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/0493468960c1243bad71ff932fbf89c17870b07cd3cb25b9565661689e52e93948e43cbd423f9903bdd80c40b98c28e4b2d85698e9ef09d4c59e23beb9140bda
+ languageName: node
+ linkType: hard
+
+"@firebase/messaging-compat@npm:0.2.15":
+ version: 0.2.15
+ resolution: "@firebase/messaging-compat@npm:0.2.15"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/messaging": "npm:0.12.15"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/dc91420e63a6a0bfaeb8d58d15dae572aa0f2bbd3c579d62eb9061fae7513738a159170990e54cd980ee194eb7a5b100cbbaf5a431b935638ecf7b5962076490
+ languageName: node
+ linkType: hard
+
+"@firebase/messaging-interop-types@npm:0.2.3":
+ version: 0.2.3
+ resolution: "@firebase/messaging-interop-types@npm:0.2.3"
+ checksum: 10c0/a6fb8f02db6a93f277cb5bd530934509e49465f775f2b5ed159116d9ce30b6255213781639b98984ff8b424a8fc36a8e5779e0cc3f0cf5e1bdbd41ae938d6c39
+ languageName: node
+ linkType: hard
+
+"@firebase/messaging@npm:0.12.15":
+ version: 0.12.15
+ resolution: "@firebase/messaging@npm:0.12.15"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/installations": "npm:0.6.11"
+ "@firebase/messaging-interop-types": "npm:0.2.3"
+ "@firebase/util": "npm:1.10.2"
+ idb: "npm:7.1.1"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/d3922785d8cbc83f5656250a75911e10d74c05337d67b79188dd8049917de97bd73ad7e15b335dd9680b2c0813dadf52e29864dcaf50f465f2a551223abb9231
+ languageName: node
+ linkType: hard
+
+"@firebase/performance-compat@npm:0.2.11":
+ version: 0.2.11
+ resolution: "@firebase/performance-compat@npm:0.2.11"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/performance": "npm:0.6.11"
+ "@firebase/performance-types": "npm:0.2.3"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/6a84e73d6a92cd892b0d6f6feaf0f65198f2660c8c0774d1d5202680563858ba44a4467d0d8ad688e9cbba77e58b9a731cb5756efccf3db6622a2b42265cde1a
+ languageName: node
+ linkType: hard
+
+"@firebase/performance-types@npm:0.2.3":
+ version: 0.2.3
+ resolution: "@firebase/performance-types@npm:0.2.3"
+ checksum: 10c0/971d6bff448481dd5e8ff9d643e14b364ed4d619aca1d8d64105555c7f4566c9c05bca3cd0c027b3f879cccf8c7bc0e31579f7f0d7b8b1de182af804572b2374
+ languageName: node
+ linkType: hard
+
+"@firebase/performance@npm:0.6.11":
+ version: 0.6.11
+ resolution: "@firebase/performance@npm:0.6.11"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/installations": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/4a4788d212e0cd7cdd2fe5623d71b7feac177fb4567f750ed23f0994ea960f77f8beb7051721e77fc44b6f40194aca33567c6c2139aa576736df36ea4934a608
+ languageName: node
+ linkType: hard
+
+"@firebase/remote-config-compat@npm:0.2.11":
+ version: 0.2.11
+ resolution: "@firebase/remote-config-compat@npm:0.2.11"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/remote-config": "npm:0.4.11"
+ "@firebase/remote-config-types": "npm:0.3.3"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/49e8ee380c7d20b98b5ea533fddb8125dbdb0f123ea40ceb17ad5a7148f432effeb2ac613c6f4ac2ec966eb6f00501da0d4c26594d414274774a99d60e9c733e
+ languageName: node
+ linkType: hard
+
+"@firebase/remote-config-types@npm:0.3.3":
+ version: 0.3.3
+ resolution: "@firebase/remote-config-types@npm:0.3.3"
+ checksum: 10c0/936ee3a5b673e424142d00e7a22788c3c6b28d068cc4fa690b203019f3f7586d1c5fe3cd520ea07744bf9ab93f25df44d0283efdb69611f6b8e02f102cdfd3eb
+ languageName: node
+ linkType: hard
+
+"@firebase/remote-config@npm:0.4.11":
+ version: 0.4.11
+ resolution: "@firebase/remote-config@npm:0.4.11"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/installations": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/6115001a7f5bd22aa1f8bb2e8c18321c53acccd7d7808555c0b414d44e26d99014a9d6f33d38e23d9949edcb6fd8bb23787326ba7fdb92b7a146841867629ed8
+ languageName: node
+ linkType: hard
+
+"@firebase/storage-compat@npm:0.3.14":
+ version: 0.3.14
+ resolution: "@firebase/storage-compat@npm:0.3.14"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/storage": "npm:0.13.4"
+ "@firebase/storage-types": "npm:0.8.3"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app-compat": 0.x
+ checksum: 10c0/e48b147e886ae7985f16c819756306664204b9dfcff7f08545ee7446bc1490d65935b11739c125a968e8462a8302b55d2f3189afc48ef0bb3b2d49256fe6df6e
+ languageName: node
+ linkType: hard
+
+"@firebase/storage-types@npm:0.8.3":
+ version: 0.8.3
+ resolution: "@firebase/storage-types@npm:0.8.3"
+ peerDependencies:
+ "@firebase/app-types": 0.x
+ "@firebase/util": 1.x
+ checksum: 10c0/4b34edca4fcbf75ba6575b02d823f5f5b0680977488a2e8101116313903d75973623cf4440f1e0f8048158e0804d0f5a7730f15bbe5af4ceb35fae6ff532a696
+ languageName: node
+ linkType: hard
+
+"@firebase/storage@npm:0.13.4":
+ version: 0.13.4
+ resolution: "@firebase/storage@npm:0.13.4"
+ dependencies:
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ checksum: 10c0/65d9286867a878f60271a5a51f8d6fa54a72672d741b7cb5b3263226b963c94986e976e0bd5b8aa82d9c5fe7d9a751e6f793a58a1f46130ab2d3d613379af9a8
+ languageName: node
+ linkType: hard
+
+"@firebase/util@npm:1.10.2":
+ version: 1.10.2
+ resolution: "@firebase/util@npm:1.10.2"
+ dependencies:
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/d6abb471948517cc9c560ebbb44e9e135716829c3abcd248a1af8aa111e48311ab410b693adc8f3bfe3b564896da7000dd7e26e34ecf59326f3b204a6a8b123c
+ languageName: node
+ linkType: hard
+
+"@firebase/vertexai@npm:1.0.2":
+ version: 1.0.2
+ resolution: "@firebase/vertexai@npm:1.0.2"
+ dependencies:
+ "@firebase/app-check-interop-types": "npm:0.3.3"
+ "@firebase/component": "npm:0.6.11"
+ "@firebase/logger": "npm:0.4.4"
+ "@firebase/util": "npm:1.10.2"
+ tslib: "npm:^2.1.0"
+ peerDependencies:
+ "@firebase/app": 0.x
+ "@firebase/app-types": 0.x
+ checksum: 10c0/65bf3482aafcd85be75b23be6edfd89d8d4d2ac7681524e961ec4df31e9df33e6d17696b6d829e01ff6d71b5410a858f89148198a03ed3c7fe8ad8c8c79bcffc
+ languageName: node
+ linkType: hard
+
+"@firebase/webchannel-wrapper@npm:1.0.3":
+ version: 1.0.3
+ resolution: "@firebase/webchannel-wrapper@npm:1.0.3"
+ checksum: 10c0/faa1e53ea82ab6bda0b9dcc5f525101a301c74d1cffb924269de947a46511a633662dd6ee8ca571470e06642b35a596625228c766f37cc2d657321edfc560d28
+ languageName: node
+ linkType: hard
+
+"@grpc/grpc-js@npm:~1.9.0":
+ version: 1.9.15
+ resolution: "@grpc/grpc-js@npm:1.9.15"
+ dependencies:
+ "@grpc/proto-loader": "npm:^0.7.8"
+ "@types/node": "npm:>=12.12.47"
+ checksum: 10c0/5bd40e1b886df238f8ffe4cab694ceb51250f94ede7da6f94233b4d9a2526a4e525aafbc8f319850c2d8126c189232be458991768877b2af441f0234fb4b4292
+ languageName: node
+ linkType: hard
+
+"@grpc/proto-loader@npm:^0.7.8":
+ version: 0.7.13
+ resolution: "@grpc/proto-loader@npm:0.7.13"
+ dependencies:
+ lodash.camelcase: "npm:^4.3.0"
+ long: "npm:^5.0.0"
+ protobufjs: "npm:^7.2.5"
+ yargs: "npm:^17.7.2"
+ bin:
+ proto-loader-gen-types: build/bin/proto-loader-gen-types.js
+ checksum: 10c0/dc8ed7aa1454c15e224707cc53d84a166b98d76f33606a9f334c7a6fb1aedd3e3614dcd2c2b02a6ffaf140587d19494f93b3a56346c6c2e26bc564f6deddbbf3
+ languageName: node
+ linkType: hard
+
"@humanfs/core@npm:^0.19.1":
version: 0.19.1
resolution: "@humanfs/core@npm:0.19.1"
@@ -1703,6 +2265,79 @@ __metadata:
languageName: node
linkType: hard
+"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2":
+ version: 1.1.2
+ resolution: "@protobufjs/aspromise@npm:1.1.2"
+ checksum: 10c0/a83343a468ff5b5ec6bff36fd788a64c839e48a07ff9f4f813564f58caf44d011cd6504ed2147bf34835bd7a7dd2107052af755961c6b098fd8902b4f6500d0f
+ languageName: node
+ linkType: hard
+
+"@protobufjs/base64@npm:^1.1.2":
+ version: 1.1.2
+ resolution: "@protobufjs/base64@npm:1.1.2"
+ checksum: 10c0/eec925e681081af190b8ee231f9bad3101e189abbc182ff279da6b531e7dbd2a56f1f306f37a80b1be9e00aa2d271690d08dcc5f326f71c9eed8546675c8caf6
+ languageName: node
+ linkType: hard
+
+"@protobufjs/codegen@npm:^2.0.4":
+ version: 2.0.4
+ resolution: "@protobufjs/codegen@npm:2.0.4"
+ checksum: 10c0/26ae337c5659e41f091606d16465bbcc1df1f37cc1ed462438b1f67be0c1e28dfb2ca9f294f39100c52161aef82edf758c95d6d75650a1ddf31f7ddee1440b43
+ languageName: node
+ linkType: hard
+
+"@protobufjs/eventemitter@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "@protobufjs/eventemitter@npm:1.1.0"
+ checksum: 10c0/1eb0a75180e5206d1033e4138212a8c7089a3d418c6dfa5a6ce42e593a4ae2e5892c4ef7421f38092badba4040ea6a45f0928869989411001d8c1018ea9a6e70
+ languageName: node
+ linkType: hard
+
+"@protobufjs/fetch@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "@protobufjs/fetch@npm:1.1.0"
+ dependencies:
+ "@protobufjs/aspromise": "npm:^1.1.1"
+ "@protobufjs/inquire": "npm:^1.1.0"
+ checksum: 10c0/cda6a3dc2d50a182c5865b160f72077aac197046600091dbb005dd0a66db9cce3c5eaed6d470ac8ed49d7bcbeef6ee5f0bc288db5ff9a70cbd003e5909065233
+ languageName: node
+ linkType: hard
+
+"@protobufjs/float@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "@protobufjs/float@npm:1.0.2"
+ checksum: 10c0/18f2bdede76ffcf0170708af15c9c9db6259b771e6b84c51b06df34a9c339dbbeec267d14ce0bddd20acc142b1d980d983d31434398df7f98eb0c94a0eb79069
+ languageName: node
+ linkType: hard
+
+"@protobufjs/inquire@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "@protobufjs/inquire@npm:1.1.0"
+ checksum: 10c0/64372482efcba1fb4d166a2664a6395fa978b557803857c9c03500e0ac1013eb4b1aacc9ed851dd5fc22f81583670b4f4431bae186f3373fedcfde863ef5921a
+ languageName: node
+ linkType: hard
+
+"@protobufjs/path@npm:^1.1.2":
+ version: 1.1.2
+ resolution: "@protobufjs/path@npm:1.1.2"
+ checksum: 10c0/cece0a938e7f5dfd2fa03f8c14f2f1cf8b0d6e13ac7326ff4c96ea311effd5fb7ae0bba754fbf505312af2e38500250c90e68506b97c02360a43793d88a0d8b4
+ languageName: node
+ linkType: hard
+
+"@protobufjs/pool@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "@protobufjs/pool@npm:1.1.0"
+ checksum: 10c0/eda2718b7f222ac6e6ad36f758a92ef90d26526026a19f4f17f668f45e0306a5bd734def3f48f51f8134ae0978b6262a5c517c08b115a551756d1a3aadfcf038
+ languageName: node
+ linkType: hard
+
+"@protobufjs/utf8@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "@protobufjs/utf8@npm:1.1.0"
+ checksum: 10c0/a3fe31fe3fa29aa3349e2e04ee13dc170cc6af7c23d92ad49e3eeaf79b9766264544d3da824dba93b7855bd6a2982fb40032ef40693da98a136d835752beb487
+ languageName: node
+ linkType: hard
+
"@rollup/plugin-babel@npm:^5.2.0":
version: 5.3.1
resolution: "@rollup/plugin-babel@npm:5.3.1"
@@ -2213,6 +2848,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0":
+ version: 22.10.2
+ resolution: "@types/node@npm:22.10.2"
+ dependencies:
+ undici-types: "npm:~6.20.0"
+ checksum: 10c0/2c7b71a040f1ef5320938eca8ebc946e6905caa9bbf3d5665d9b3774a8d15ea9fab1582b849a6d28c7fc80756a62c5666bc66b69f42f4d5dafd1ccb193cdb4ac
+ languageName: node
+ linkType: hard
+
"@types/node@npm:^20.17.10":
version: 20.17.10
resolution: "@types/node@npm:20.17.10"
@@ -3220,6 +3864,17 @@ __metadata:
languageName: node
linkType: hard
+"cliui@npm:^8.0.1":
+ version: 8.0.1
+ resolution: "cliui@npm:8.0.1"
+ dependencies:
+ string-width: "npm:^4.2.0"
+ strip-ansi: "npm:^6.0.1"
+ wrap-ansi: "npm:^7.0.0"
+ checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5
+ languageName: node
+ linkType: hard
+
"color-convert@npm:^2.0.1":
version: 2.0.1
resolution: "color-convert@npm:2.0.1"
@@ -3889,7 +4544,7 @@ __metadata:
languageName: node
linkType: hard
-"escalade@npm:^3.2.0":
+"escalade@npm:^3.1.1, escalade@npm:^3.2.0":
version: 3.2.0
resolution: "escalade@npm:3.2.0"
checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65
@@ -4281,6 +4936,15 @@ __metadata:
languageName: node
linkType: hard
+"faye-websocket@npm:0.11.4":
+ version: 0.11.4
+ resolution: "faye-websocket@npm:0.11.4"
+ dependencies:
+ websocket-driver: "npm:>=0.5.1"
+ checksum: 10c0/c6052a0bb322778ce9f89af92890f6f4ce00d5ec92418a35e5f4c6864a4fe736fec0bcebd47eac7c0f0e979b01530746b1c85c83cb04bae789271abf19737420
+ languageName: node
+ linkType: hard
+
"fdir@npm:^6.4.2":
version: 6.4.2
resolution: "fdir@npm:6.4.2"
@@ -4339,6 +5003,42 @@ __metadata:
languageName: node
linkType: hard
+"firebase@npm:^11.1.0":
+ version: 11.1.0
+ resolution: "firebase@npm:11.1.0"
+ dependencies:
+ "@firebase/analytics": "npm:0.10.10"
+ "@firebase/analytics-compat": "npm:0.2.16"
+ "@firebase/app": "npm:0.10.17"
+ "@firebase/app-check": "npm:0.8.10"
+ "@firebase/app-check-compat": "npm:0.3.17"
+ "@firebase/app-compat": "npm:0.2.47"
+ "@firebase/app-types": "npm:0.9.3"
+ "@firebase/auth": "npm:1.8.1"
+ "@firebase/auth-compat": "npm:0.5.16"
+ "@firebase/data-connect": "npm:0.1.3"
+ "@firebase/database": "npm:1.0.10"
+ "@firebase/database-compat": "npm:2.0.1"
+ "@firebase/firestore": "npm:4.7.5"
+ "@firebase/firestore-compat": "npm:0.3.40"
+ "@firebase/functions": "npm:0.12.0"
+ "@firebase/functions-compat": "npm:0.3.17"
+ "@firebase/installations": "npm:0.6.11"
+ "@firebase/installations-compat": "npm:0.2.11"
+ "@firebase/messaging": "npm:0.12.15"
+ "@firebase/messaging-compat": "npm:0.2.15"
+ "@firebase/performance": "npm:0.6.11"
+ "@firebase/performance-compat": "npm:0.2.11"
+ "@firebase/remote-config": "npm:0.4.11"
+ "@firebase/remote-config-compat": "npm:0.2.11"
+ "@firebase/storage": "npm:0.13.4"
+ "@firebase/storage-compat": "npm:0.3.14"
+ "@firebase/util": "npm:1.10.2"
+ "@firebase/vertexai": "npm:1.0.2"
+ checksum: 10c0/d1bbd880331700a85ee24d2409fd0c06a50d2c3b1c348e492848360bda3f9ede3358b8c793faaf69729bd0e2d19ddd08db51128e5286d65cb57a993351a966d5
+ languageName: node
+ linkType: hard
+
"flat-cache@npm:^4.0.0":
version: 4.0.1
resolution: "flat-cache@npm:4.0.1"
@@ -4488,6 +5188,13 @@ __metadata:
languageName: node
linkType: hard
+"get-caller-file@npm:^2.0.5":
+ version: 2.0.5
+ resolution: "get-caller-file@npm:2.0.5"
+ checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde
+ languageName: node
+ linkType: hard
+
"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6":
version: 1.2.6
resolution: "get-intrinsic@npm:1.2.6"
@@ -4583,6 +5290,7 @@ __metadata:
eslint-plugin-import: "npm:^2.31.0"
eslint-plugin-simple-import-sort: "npm:^12.1.1"
eslint-plugin-vue: "npm:^9.32.0"
+ firebase: "npm:^11.1.0"
pinia: "npm:^2.3.0"
pinia-plugin-persistedstate: "npm:^4.2.0"
prettier: "npm:^3.4.2"
@@ -4596,6 +5304,7 @@ __metadata:
vue: "npm:^3.5.13"
vue-router: "npm:^4.5.0"
vue-tsc: "npm:^2.2.0"
+ vuefire: "npm:^3.2.1"
vuetify: "npm:^3.7.6"
languageName: unknown
linkType: soft
@@ -4832,6 +5541,13 @@ __metadata:
languageName: node
linkType: hard
+"http-parser-js@npm:>=0.5.1":
+ version: 0.5.8
+ resolution: "http-parser-js@npm:0.5.8"
+ checksum: 10c0/4ed89f812c44f84c4ae5d43dd3a0c47942b875b63be0ed2ccecbe6b0018af867d806495fc6e12474aff868721163699c49246585bddea4f0ecc6d2b02e19faf1
+ languageName: node
+ linkType: hard
+
"http-proxy-agent@npm:^7.0.0":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
@@ -4885,7 +5601,7 @@ __metadata:
languageName: node
linkType: hard
-"idb@npm:^7.0.1":
+"idb@npm:7.1.1, idb@npm:^7.0.1":
version: 7.1.1
resolution: "idb@npm:7.1.1"
checksum: 10c0/72418e4397638797ee2089f97b45fc29f937b830bc0eb4126f4a9889ecf10320ceacf3a177fe5d7ffaf6b4fe38b20bbd210151549bfdc881db8081eed41c870d
@@ -5535,6 +6251,13 @@ __metadata:
languageName: node
linkType: hard
+"lodash.camelcase@npm:^4.3.0":
+ version: 4.3.0
+ resolution: "lodash.camelcase@npm:4.3.0"
+ checksum: 10c0/fcba15d21a458076dd309fce6b1b4bf611d84a0ec252cb92447c948c533ac250b95d2e00955801ebc367e5af5ed288b996d75d37d2035260a937008e14eaf432
+ languageName: node
+ linkType: hard
+
"lodash.debounce@npm:^4.0.8":
version: 4.0.8
resolution: "lodash.debounce@npm:4.0.8"
@@ -5563,6 +6286,13 @@ __metadata:
languageName: node
linkType: hard
+"long@npm:^5.0.0":
+ version: 5.2.3
+ resolution: "long@npm:5.2.3"
+ checksum: 10c0/6a0da658f5ef683b90330b1af76f06790c623e148222da9d75b60e266bbf88f803232dd21464575681638894a84091616e7f89557aa087fd14116c0f4e0e43d9
+ languageName: node
+ linkType: hard
+
"lower-case@npm:^2.0.2":
version: 2.0.2
resolution: "lower-case@npm:2.0.2"
@@ -6477,6 +7207,26 @@ __metadata:
languageName: node
linkType: hard
+"protobufjs@npm:^7.2.5":
+ version: 7.4.0
+ resolution: "protobufjs@npm:7.4.0"
+ dependencies:
+ "@protobufjs/aspromise": "npm:^1.1.2"
+ "@protobufjs/base64": "npm:^1.1.2"
+ "@protobufjs/codegen": "npm:^2.0.4"
+ "@protobufjs/eventemitter": "npm:^1.1.0"
+ "@protobufjs/fetch": "npm:^1.1.0"
+ "@protobufjs/float": "npm:^1.0.2"
+ "@protobufjs/inquire": "npm:^1.1.0"
+ "@protobufjs/path": "npm:^1.1.2"
+ "@protobufjs/pool": "npm:^1.1.0"
+ "@protobufjs/utf8": "npm:^1.1.0"
+ "@types/node": "npm:>=13.7.0"
+ long: "npm:^5.0.0"
+ checksum: 10c0/a5460a63fe596523b9a067cbce39a6b310d1a71750fda261f076535662aada97c24450e18c5bc98a27784f70500615904ff1227e1742183509f0db4fdede669b
+ languageName: node
+ linkType: hard
+
"proxy-from-env@npm:^1.1.0":
version: 1.1.0
resolution: "proxy-from-env@npm:1.1.0"
@@ -6639,6 +7389,13 @@ __metadata:
languageName: node
linkType: hard
+"require-directory@npm:^2.1.1":
+ version: 2.1.1
+ resolution: "require-directory@npm:2.1.1"
+ checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99
+ languageName: node
+ linkType: hard
+
"require-from-string@npm:^2.0.2":
version: 2.0.2
resolution: "require-from-string@npm:2.0.2"
@@ -6826,7 +7583,7 @@ __metadata:
languageName: node
linkType: hard
-"safe-buffer@npm:^5.1.0":
+"safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.1.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
@@ -7115,7 +7872,7 @@ __metadata:
languageName: node
linkType: hard
-"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0":
+"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
version: 4.2.3
resolution: "string-width@npm:4.2.3"
dependencies:
@@ -7435,7 +8192,7 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^2.0.3, tslib@npm:^2.6.2":
+"tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.6.2":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
@@ -7597,6 +8354,13 @@ __metadata:
languageName: node
linkType: hard
+"undici-types@npm:~6.20.0":
+ version: 6.20.0
+ resolution: "undici-types@npm:6.20.0"
+ checksum: 10c0/68e659a98898d6a836a9a59e6adf14a5d799707f5ea629433e025ac90d239f75e408e2e5ff086afc3cace26f8b26ee52155293564593fbb4a2f666af57fc59bf
+ languageName: node
+ linkType: hard
+
"unicode-canonical-property-names-ecmascript@npm:^2.0.0":
version: 2.0.1
resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.1"
@@ -7976,7 +8740,7 @@ __metadata:
languageName: node
linkType: hard
-"vue-demi@npm:^0.14.10":
+"vue-demi@npm:^0.14.10, vue-demi@npm:latest":
version: 0.14.10
resolution: "vue-demi@npm:0.14.10"
peerDependencies:
@@ -8052,6 +8816,26 @@ __metadata:
languageName: node
linkType: hard
+"vuefire@npm:^3.2.1":
+ version: 3.2.1
+ resolution: "vuefire@npm:3.2.1"
+ dependencies:
+ vue-demi: "npm:latest"
+ peerDependencies:
+ consola: ^3.2.3
+ firebase: ^9.0.0 || ^10.0.0 || ^11.0.0
+ vue: ^2.7.0 || ^3.2.0
+ peerDependenciesMeta:
+ "@vue/composition-api":
+ optional: true
+ consola:
+ optional: true
+ firebase:
+ optional: true
+ checksum: 10c0/26760c3268dcaa91c86b43e799fedfcaaa9f4912b920735626f8b434fc0beb2019a5d4b71f537cb17d1a88ca69cef6ef60b5c60325e184deddeaef957615bf8f
+ languageName: node
+ linkType: hard
+
"vuetify@npm:^3.7.6":
version: 3.7.6
resolution: "vuetify@npm:3.7.6"
@@ -8106,6 +8890,24 @@ __metadata:
languageName: node
linkType: hard
+"websocket-driver@npm:>=0.5.1":
+ version: 0.7.4
+ resolution: "websocket-driver@npm:0.7.4"
+ dependencies:
+ http-parser-js: "npm:>=0.5.1"
+ safe-buffer: "npm:>=5.1.0"
+ websocket-extensions: "npm:>=0.1.1"
+ checksum: 10c0/5f09547912b27bdc57bac17b7b6527d8993aa4ac8a2d10588bb74aebaf785fdcf64fea034aae0c359b7adff2044dd66f3d03866e4685571f81b13e548f9021f1
+ languageName: node
+ linkType: hard
+
+"websocket-extensions@npm:>=0.1.1":
+ version: 0.1.4
+ resolution: "websocket-extensions@npm:0.1.4"
+ checksum: 10c0/bbc8c233388a0eb8a40786ee2e30d35935cacbfe26ab188b3e020987e85d519c2009fe07cfc37b7f718b85afdba7e54654c9153e6697301f72561bfe429177e0
+ languageName: node
+ linkType: hard
+
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
@@ -8406,7 +9208,7 @@ __metadata:
languageName: node
linkType: hard
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0":
version: 7.0.0
resolution: "wrap-ansi@npm:7.0.0"
dependencies:
@@ -8442,6 +9244,13 @@ __metadata:
languageName: node
linkType: hard
+"y18n@npm:^5.0.5":
+ version: 5.0.8
+ resolution: "y18n@npm:5.0.8"
+ checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249
+ languageName: node
+ linkType: hard
+
"yallist@npm:^3.0.2":
version: 3.1.1
resolution: "yallist@npm:3.1.1"
@@ -8463,6 +9272,28 @@ __metadata:
languageName: node
linkType: hard
+"yargs-parser@npm:^21.1.1":
+ version: 21.1.1
+ resolution: "yargs-parser@npm:21.1.1"
+ checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2
+ languageName: node
+ linkType: hard
+
+"yargs@npm:^17.7.2":
+ version: 17.7.2
+ resolution: "yargs@npm:17.7.2"
+ dependencies:
+ cliui: "npm:^8.0.1"
+ escalade: "npm:^3.1.1"
+ get-caller-file: "npm:^2.0.5"
+ require-directory: "npm:^2.1.1"
+ string-width: "npm:^4.2.3"
+ y18n: "npm:^5.0.5"
+ yargs-parser: "npm:^21.1.1"
+ checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05
+ languageName: node
+ linkType: hard
+
"yocto-queue@npm:^0.1.0":
version: 0.1.0
resolution: "yocto-queue@npm:0.1.0"