diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a6be6d0da4..4012015131 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,9 @@ }, "ghcr.io/devcontainers/features/powershell:1.1.0": {}, "ghcr.io/devcontainers/features/azure-cli:1.0.8": {}, - "ghcr.io/azure/azure-dev/azd:latest": {} + "ghcr.io/azure/azure-dev/azd:latest": {}, + // Required for azd to package the app to ACA + "ghcr.io/devcontainers/features/docker-in-docker:2": {} }, "customizations": { "vscode": { diff --git a/app/backend/Dockerfile b/app/backend/Dockerfile new file mode 100644 index 0000000000..2289cf5487 --- /dev/null +++ b/app/backend/Dockerfile @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/devcontainers/python:3.11-bullseye + +RUN python -m pip install --upgrade pip + +WORKDIR /demo-code + +COPY requirements.txt . +RUN python -m pip install -r requirements.txt +RUN python -m pip install gunicorn==21.2.0 + +COPY entrypoint.sh . +RUN chmod +x entrypoint.sh + +COPY . . +CMD bash -c ". entrypoint.sh" diff --git a/app/backend/app.py b/app/backend/app.py index 1a02dd7eaf..258e498c8c 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -6,7 +6,7 @@ import aiohttp import openai -from azure.identity.aio import DefaultAzureCredential +from azure.identity.aio import DefaultAzureCredential, ManagedIdentityCredential from azure.monitor.opentelemetry import configure_azure_monitor from azure.search.documents.aio import SearchClient from azure.storage.blob.aio import BlobServiceClient @@ -131,7 +131,10 @@ async def setup_clients(): # just use 'az login' locally, and managed identity when deployed on Azure). If you need to use keys, use separate AzureKeyCredential instances with the # keys for each service # If you encounter a blocking error during a DefaultAzureCredential resolution, you can exclude the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) - azure_credential = DefaultAzureCredential(exclude_shared_token_cache_credential = True) + if os.getenv("AZURE_IDENTITY_ID"): + azure_credential = ManagedIdentityCredential(client_id=os.getenv("AZURE_IDENTITY_ID")) + else: + azure_credential = DefaultAzureCredential(exclude_shared_token_cache_credential = True) # Set up clients for Cognitive Search and Storage search_client = SearchClient( diff --git a/app/backend/entrypoint.sh b/app/backend/entrypoint.sh new file mode 100644 index 0000000000..1b84b9a526 --- /dev/null +++ b/app/backend/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python3 -m gunicorn main:app diff --git a/app/doc_uploader/.dockerignore b/app/doc_uploader/.dockerignore new file mode 100644 index 0000000000..7ff5fcf542 --- /dev/null +++ b/app/doc_uploader/.dockerignore @@ -0,0 +1,29 @@ +**/__pycache__ +**/.venv +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +**/.parcel-cache +**/dist \ No newline at end of file diff --git a/app/doc_uploader/Dockerfile b/app/doc_uploader/Dockerfile new file mode 100644 index 0000000000..cdb58b8a97 --- /dev/null +++ b/app/doc_uploader/Dockerfile @@ -0,0 +1,35 @@ +FROM node:20-slim AS build + +WORKDIR /app +COPY . . +WORKDIR /app/client +RUN npm install && npx parcel build index.html --dist-dir ../dist +WORKDIR /app +RUN rm -rf client + +# For more information, please refer to https://aka.ms/vscode-docker-python +FROM python:3.10-slim + +EXPOSE 8000 + +# Keeps Python from generating .pyc files in the container +ENV PYTHONDONTWRITEBYTECODE=1 + +# Turns off buffering for easier container logging +ENV PYTHONUNBUFFERED=1 + +# Install pip requirements +COPY --from=build /app/requirements.txt . +RUN python -m pip install -r requirements.txt + +WORKDIR /app +COPY --from=build /app /app + +# Creates a non-root user with an explicit UID and adds permission to access the /app folder +# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers +RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app +USER appuser + +# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "-k", "uvicorn.workers.UvicornWorker", "main:app"] +# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/app/doc_uploader/build-client.sh b/app/doc_uploader/build-client.sh new file mode 100755 index 0000000000..05f904ac1d --- /dev/null +++ b/app/doc_uploader/build-client.sh @@ -0,0 +1,3 @@ +cd client +npx parcel build index.html --dist-dir ../dist +cd .. \ No newline at end of file diff --git a/app/doc_uploader/client/.gitignore b/app/doc_uploader/client/.gitignore new file mode 100644 index 0000000000..dce36cb63e --- /dev/null +++ b/app/doc_uploader/client/.gitignore @@ -0,0 +1 @@ +.parcel-cache \ No newline at end of file diff --git a/app/doc_uploader/client/index.html b/app/doc_uploader/client/index.html new file mode 100644 index 0000000000..1fbe0cb0e4 --- /dev/null +++ b/app/doc_uploader/client/index.html @@ -0,0 +1,14 @@ + + + + + + + + +

+
+
+
+
+
\ No newline at end of file
diff --git a/app/doc_uploader/client/index.js b/app/doc_uploader/client/index.js
new file mode 100644
index 0000000000..e52270c2f7
--- /dev/null
+++ b/app/doc_uploader/client/index.js
@@ -0,0 +1,43 @@
+const { BlockBlobClient } = require("@azure/storage-blob");
+
+const fileInput = document.getElementById("file-input");
+const uploadButton = document.getElementById("upload-button");
+const progress = document.getElementById("progress");
+
+uploadButton.addEventListener("click", async () => {
+    progress.innerText = "Uploading...";
+
+    const file = fileInput.files[0];
+    const blobName = file.name;
+    const { sasUrl } = await fetch(`/sas?filename=${blobName}`).then((res) => res.json());
+
+    const blobClient = new BlockBlobClient(sasUrl);
+    await blobClient.uploadBrowserData(file);
+
+    progress.innerText = "Uploaded! Starting job...";
+
+    const jobInfo = await fetch(`/startjob?filename=${blobName}`, { method: "POST" })
+        .then((res) => res.json());
+
+    console.log(JSON.stringify(jobInfo, null, 2));
+
+    const jobName = jobInfo.result?.name;
+    if (jobName) {
+        progress.innerText = `Job ${jobName} started. Waiting for output...`;
+    }
+
+    const logFileUrl = jobInfo.logFileUrl;
+    let tries = 0;
+    while(tries++ < 500) {
+        const logFileResponse = await fetch(logFileUrl);
+        if (logFileResponse.status === 200) {
+            progress.innerText = await logFileResponse.text();
+            window.scrollTo(0, document.body.scrollHeight);
+            if (progress.innerText.match(/Indexed \d+ sections, \d+ succeeded/i)) {
+                break;
+            }
+        }
+        await new Promise((resolve) => setTimeout(resolve, 2000));
+    }
+});
+
diff --git a/app/doc_uploader/client/package-lock.json b/app/doc_uploader/client/package-lock.json
new file mode 100644
index 0000000000..b3fea33265
--- /dev/null
+++ b/app/doc_uploader/client/package-lock.json
@@ -0,0 +1,3681 @@
+{
+  "name": "static",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "static",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "@azure/storage-blob": "^12.15.0",
+        "parcel": "^2.9.3"
+      },
+      "devDependencies": {
+        "buffer": "^6.0.3"
+      }
+    },
+    "node_modules/@azure/abort-controller": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
+      "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==",
+      "dependencies": {
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/@azure/core-auth": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz",
+      "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==",
+      "dependencies": {
+        "@azure/abort-controller": "^1.0.0",
+        "@azure/core-util": "^1.1.0",
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@azure/core-http": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.3.tgz",
+      "integrity": "sha512-QMib3wXotJMFhHgmJBPUF9YsyErw34H0XDFQd9CauH7TPB+RGcyl9Ayy7iURtJB04ngXhE6YwrQsWDXlSLrilg==",
+      "dependencies": {
+        "@azure/abort-controller": "^1.0.0",
+        "@azure/core-auth": "^1.3.0",
+        "@azure/core-tracing": "1.0.0-preview.13",
+        "@azure/core-util": "^1.1.1",
+        "@azure/logger": "^1.0.0",
+        "@types/node-fetch": "^2.5.0",
+        "@types/tunnel": "^0.0.3",
+        "form-data": "^4.0.0",
+        "node-fetch": "^2.6.7",
+        "process": "^0.11.10",
+        "tslib": "^2.2.0",
+        "tunnel": "^0.0.6",
+        "uuid": "^8.3.0",
+        "xml2js": "^0.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@azure/core-lro": {
+      "version": "2.5.4",
+      "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz",
+      "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==",
+      "dependencies": {
+        "@azure/abort-controller": "^1.0.0",
+        "@azure/core-util": "^1.2.0",
+        "@azure/logger": "^1.0.0",
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@azure/core-paging": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz",
+      "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==",
+      "dependencies": {
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@azure/core-tracing": {
+      "version": "1.0.0-preview.13",
+      "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz",
+      "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==",
+      "dependencies": {
+        "@opentelemetry/api": "^1.0.1",
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/@azure/core-util": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz",
+      "integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==",
+      "dependencies": {
+        "@azure/abort-controller": "^1.0.0",
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@azure/logger": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz",
+      "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==",
+      "dependencies": {
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@azure/storage-blob": {
+      "version": "12.15.0",
+      "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.15.0.tgz",
+      "integrity": "sha512-e7JBKLOFi0QVJqqLzrjx1eL3je3/Ug2IQj24cTM9b85CsnnFjLGeGjJVIjbGGZaytewiCEG7r3lRwQX7fKj0/w==",
+      "dependencies": {
+        "@azure/abort-controller": "^1.0.0",
+        "@azure/core-http": "^3.0.0",
+        "@azure/core-lro": "^2.2.0",
+        "@azure/core-paging": "^1.1.1",
+        "@azure/core-tracing": "1.0.0-preview.13",
+        "@azure/logger": "^1.0.0",
+        "events": "^3.0.0",
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.22.13",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
+      "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
+      "dependencies": {
+        "@babel/highlight": "^7.22.13",
+        "chalk": "^2.4.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+    },
+    "node_modules/@babel/code-frame/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.22.15",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz",
+      "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight": {
+      "version": "7.22.13",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz",
+      "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.22.5",
+        "chalk": "^2.4.2",
+        "js-tokens": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+    },
+    "node_modules/@babel/highlight/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@lezer/common": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz",
+      "integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig=="
+    },
+    "node_modules/@lezer/lr": {
+      "version": "0.15.8",
+      "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz",
+      "integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==",
+      "dependencies": {
+        "@lezer/common": "^0.15.0"
+      }
+    },
+    "node_modules/@lmdb/lmdb-darwin-arm64": {
+      "version": "2.7.11",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.7.11.tgz",
+      "integrity": "sha512-r6+vYq2vKzE+vgj/rNVRMwAevq0+ZR9IeMFIqcSga+wMtMdXQ27KqQ7uS99/yXASg29bos7yHP3yk4x6Iio0lw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-darwin-x64": {
+      "version": "2.7.11",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.7.11.tgz",
+      "integrity": "sha512-jhj1aB4K8ycRL1HOQT5OtzlqOq70jxUQEWRN9Gqh3TIDN30dxXtiHi6EWF516tzw6v2+3QqhDMJh8O6DtTGG8Q==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm": {
+      "version": "2.7.11",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.7.11.tgz",
+      "integrity": "sha512-dHfLFVSrw/v5X5lkwp0Vl7+NFpEeEYKfMG2DpdFJnnG1RgHQZngZxCaBagFoaJGykRpd2DYF1AeuXBFrAUAXfw==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm64": {
+      "version": "2.7.11",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.7.11.tgz",
+      "integrity": "sha512-7xGEfPPbmVJWcY2Nzqo11B9Nfxs+BAsiiaY/OcT4aaTDdykKeCjvKMQJA3KXCtZ1AtiC9ljyGLi+BfUwdulY5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-x64": {
+      "version": "2.7.11",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.7.11.tgz",
+      "integrity": "sha512-vUKI3JrREMQsXX8q0Eq5zX2FlYCKWMmLiCyyJNfZK0Uyf14RBg9VtB3ObQ41b4swYh2EWaltasWVe93Y8+KDng==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-win32-x64": {
+      "version": "2.7.11",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.7.11.tgz",
+      "integrity": "sha512-BJwkHlSUgtB+Ei52Ai32M1AOMerSlzyIGA/KC4dAGL+GGwVMdwG8HGCOA2TxP3KjhbgDPMYkv7bt/NmOmRIFng==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@mischnic/json-sourcemap": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz",
+      "integrity": "sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==",
+      "dependencies": {
+        "@lezer/common": "^0.15.7",
+        "@lezer/lr": "^0.15.4",
+        "json5": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz",
+      "integrity": "sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz",
+      "integrity": "sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz",
+      "integrity": "sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz",
+      "integrity": "sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz",
+      "integrity": "sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz",
+      "integrity": "sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@opentelemetry/api": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz",
+      "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@parcel/bundler-default": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.9.3.tgz",
+      "integrity": "sha512-JjJK8dq39/UO/MWI/4SCbB1t/qgpQRFnFDetAAAezQ8oN++b24u1fkMDa/xqQGjbuPmGeTds5zxGgYs7id7PYg==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/graph": "2.9.3",
+        "@parcel/hash": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/cache": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.9.3.tgz",
+      "integrity": "sha512-Bj/H2uAJJSXtysG7E/x4EgTrE2hXmm7td/bc97K8M9N7+vQjxf7xb0ebgqe84ePVMkj4MVQSMEJkEucXVx4b0Q==",
+      "dependencies": {
+        "@parcel/fs": "2.9.3",
+        "@parcel/logger": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "lmdb": "2.7.11"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@parcel/codeframe": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.9.3.tgz",
+      "integrity": "sha512-z7yTyD6h3dvduaFoHpNqur74/2yDWL++33rjQjIjCaXREBN6dKHoMGMizzo/i4vbiI1p9dDox2FIDEHCMQxqdA==",
+      "dependencies": {
+        "chalk": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/compressor-raw": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.9.3.tgz",
+      "integrity": "sha512-jz3t4/ICMsHEqgiTmv5i1DJva2k5QRpZlBELVxfY+QElJTVe8edKJ0TiKcBxh2hx7sm4aUigGmp7JiqqHRRYmA==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/config-default": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.9.3.tgz",
+      "integrity": "sha512-tqN5tF7QnVABDZAu76co5E6N8mA9n8bxiWdK4xYyINYFIEHgX172oRTqXTnhEMjlMrdmASxvnGlbaPBaVnrCTw==",
+      "dependencies": {
+        "@parcel/bundler-default": "2.9.3",
+        "@parcel/compressor-raw": "2.9.3",
+        "@parcel/namer-default": "2.9.3",
+        "@parcel/optimizer-css": "2.9.3",
+        "@parcel/optimizer-htmlnano": "2.9.3",
+        "@parcel/optimizer-image": "2.9.3",
+        "@parcel/optimizer-svgo": "2.9.3",
+        "@parcel/optimizer-swc": "2.9.3",
+        "@parcel/packager-css": "2.9.3",
+        "@parcel/packager-html": "2.9.3",
+        "@parcel/packager-js": "2.9.3",
+        "@parcel/packager-raw": "2.9.3",
+        "@parcel/packager-svg": "2.9.3",
+        "@parcel/reporter-dev-server": "2.9.3",
+        "@parcel/resolver-default": "2.9.3",
+        "@parcel/runtime-browser-hmr": "2.9.3",
+        "@parcel/runtime-js": "2.9.3",
+        "@parcel/runtime-react-refresh": "2.9.3",
+        "@parcel/runtime-service-worker": "2.9.3",
+        "@parcel/transformer-babel": "2.9.3",
+        "@parcel/transformer-css": "2.9.3",
+        "@parcel/transformer-html": "2.9.3",
+        "@parcel/transformer-image": "2.9.3",
+        "@parcel/transformer-js": "2.9.3",
+        "@parcel/transformer-json": "2.9.3",
+        "@parcel/transformer-postcss": "2.9.3",
+        "@parcel/transformer-posthtml": "2.9.3",
+        "@parcel/transformer-raw": "2.9.3",
+        "@parcel/transformer-react-refresh-wrap": "2.9.3",
+        "@parcel/transformer-svg": "2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@parcel/core": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.9.3.tgz",
+      "integrity": "sha512-4KlM1Zr/jpsqWuMXr2zmGsaOUs1zMMFh9vfCNKRZkptf+uk8I3sugHbNdo+F5B+4e2yMuOEb1zgAmvJLeuH6ww==",
+      "dependencies": {
+        "@mischnic/json-sourcemap": "^0.1.0",
+        "@parcel/cache": "2.9.3",
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/events": "2.9.3",
+        "@parcel/fs": "2.9.3",
+        "@parcel/graph": "2.9.3",
+        "@parcel/hash": "2.9.3",
+        "@parcel/logger": "2.9.3",
+        "@parcel/package-manager": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/profiler": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/types": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "@parcel/workers": "2.9.3",
+        "abortcontroller-polyfill": "^1.1.9",
+        "base-x": "^3.0.8",
+        "browserslist": "^4.6.6",
+        "clone": "^2.1.1",
+        "dotenv": "^7.0.0",
+        "dotenv-expand": "^5.1.0",
+        "json5": "^2.2.0",
+        "msgpackr": "^1.5.4",
+        "nullthrows": "^1.1.1",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/diagnostic": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.9.3.tgz",
+      "integrity": "sha512-6jxBdyB3D7gP4iE66ghUGntWt2v64E6EbD4AetZk+hNJpgudOOPsKTovcMi/i7I4V0qD7WXSF4tvkZUoac0jwA==",
+      "dependencies": {
+        "@mischnic/json-sourcemap": "^0.1.0",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/events": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.9.3.tgz",
+      "integrity": "sha512-K0Scx+Bx9f9p1vuShMzNwIgiaZUkxEnexaKYHYemJrM7pMAqxIuIqhnvwurRCsZOVLUJPDDNJ626cWTc5vIq+A==",
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/fs": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.9.3.tgz",
+      "integrity": "sha512-/PrRKgCRw22G7rNPSpgN3Q+i2nIkZWuvIOAdMG4KWXC4XLp8C9jarNaWd5QEQ75amjhQSl3oUzABzkdCtkKrgg==",
+      "dependencies": {
+        "@parcel/fs-search": "2.9.3",
+        "@parcel/types": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "@parcel/watcher": "^2.0.7",
+        "@parcel/workers": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@parcel/fs-search": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.9.3.tgz",
+      "integrity": "sha512-nsNz3bsOpwS+jphcd+XjZL3F3PDq9lik0O8HPm5f6LYkqKWT+u/kgQzA8OkAHCR3q96LGiHxUywHPEBc27vI4Q==",
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/graph": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.9.3.tgz",
+      "integrity": "sha512-3LmRJmF8+OprAr6zJT3X2s8WAhLKkrhi6RsFlMWHifGU5ED1PFcJWFbOwJvSjcAhMQJP0fErcFIK1Ludv3Vm3g==",
+      "dependencies": {
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/hash": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.9.3.tgz",
+      "integrity": "sha512-qlH5B85XLzVAeijgKPjm1gQu35LoRYX/8igsjnN8vOlbc3O8BYAUIutU58fbHbtE8MJPbxQQUw7tkTjeoujcQQ==",
+      "dependencies": {
+        "xxhash-wasm": "^0.4.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/logger": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.9.3.tgz",
+      "integrity": "sha512-5FNBszcV6ilGFcijEOvoNVG6IUJGsnMiaEnGQs7Fvc1dktTjEddnoQbIYhcSZL63wEmzBZOgkT5yDMajJ/41jw==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/events": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/markdown-ansi": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.9.3.tgz",
+      "integrity": "sha512-/Q4X8F2aN8UNjAJrQ5NfK2OmZf6shry9DqetUSEndQ0fHonk78WKt6LT0zSKEBEW/bB/bXk6mNMsCup6L8ibjQ==",
+      "dependencies": {
+        "chalk": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/namer-default": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.9.3.tgz",
+      "integrity": "sha512-1ynFEcap48/Ngzwwn318eLYpLUwijuuZoXQPCsEQ21OOIOtfhFQJaPwXTsw6kRitshKq76P2aafE0BioGSqxcA==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/node-resolver-core": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-3.0.3.tgz",
+      "integrity": "sha512-AjxNcZVHHJoNT/A99PKIdFtwvoze8PAiC3yz8E/dRggrDIOboUEodeQYV5Aq++aK76uz/iOP0tST2T8A5rhb1A==",
+      "dependencies": {
+        "@mischnic/json-sourcemap": "^0.1.0",
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/fs": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-css": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.9.3.tgz",
+      "integrity": "sha512-RK1QwcSdWDNUsFvuLy0hgnYKtPQebzCb0vPPzqs6LhL+vqUu9utOyRycGaQffHCkHVQP6zGlN+KFssd7YtFGhA==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.9.3",
+        "browserslist": "^4.6.6",
+        "lightningcss": "^1.16.1",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-htmlnano": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.9.3.tgz",
+      "integrity": "sha512-9g/KBck3c6DokmJfvJ5zpHFBiCSolaGrcsTGx8C3YPdCTVTI9P1TDCwUxvAr4LjpcIRSa82wlLCI+nF6sSgxKA==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "htmlnano": "^2.0.0",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "svgo": "^2.4.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-htmlnano/node_modules/css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/@parcel/optimizer-htmlnano/node_modules/css-tree": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+      "dependencies": {
+        "mdn-data": "2.0.14",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@parcel/optimizer-htmlnano/node_modules/csso": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
+      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+      "dependencies": {
+        "css-tree": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@parcel/optimizer-htmlnano/node_modules/mdn-data": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
+    },
+    "node_modules/@parcel/optimizer-htmlnano/node_modules/svgo": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+      "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+      "dependencies": {
+        "@trysound/sax": "0.2.0",
+        "commander": "^7.2.0",
+        "css-select": "^4.1.3",
+        "css-tree": "^1.1.3",
+        "csso": "^4.2.0",
+        "picocolors": "^1.0.0",
+        "stable": "^0.1.8"
+      },
+      "bin": {
+        "svgo": "bin/svgo"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/@parcel/optimizer-image": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.9.3.tgz",
+      "integrity": "sha512-530YzthE7kmecnNhPbkAK+26yQNt69pfJrgE0Ev0BZaM1Wu2+33nki7o8qvkTkikhPrurEJLGIXt1qKmbKvCbA==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "@parcel/workers": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@parcel/optimizer-svgo": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.9.3.tgz",
+      "integrity": "sha512-ytQS0wY5JJhWU4mL0wfhYDUuHcfuw+Gy2+JcnTm1t1AZXHlOTbU6EzRWNqBShsgXjvdrQQXizAe3B6GFFlFJVQ==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "svgo": "^2.4.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-svgo/node_modules/css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/@parcel/optimizer-svgo/node_modules/css-tree": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+      "dependencies": {
+        "mdn-data": "2.0.14",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@parcel/optimizer-svgo/node_modules/csso": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
+      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+      "dependencies": {
+        "css-tree": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@parcel/optimizer-svgo/node_modules/mdn-data": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
+    },
+    "node_modules/@parcel/optimizer-svgo/node_modules/svgo": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+      "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+      "dependencies": {
+        "@trysound/sax": "0.2.0",
+        "commander": "^7.2.0",
+        "css-select": "^4.1.3",
+        "css-tree": "^1.1.3",
+        "csso": "^4.2.0",
+        "picocolors": "^1.0.0",
+        "stable": "^0.1.8"
+      },
+      "bin": {
+        "svgo": "bin/svgo"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/@parcel/optimizer-swc": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-swc/-/optimizer-swc-2.9.3.tgz",
+      "integrity": "sha512-GQINNeqtdpL1ombq/Cpwi6IBk02wKJ/JJbYbyfHtk8lxlq13soenpwOlzJ5T9D2fdG+FUhai9NxpN5Ss4lNoAg==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.9.3",
+        "@swc/core": "^1.3.36",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/package-manager": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.9.3.tgz",
+      "integrity": "sha512-NH6omcNTEupDmW4Lm1e4NUYBjdqkURxgZ4CNESESInHJe6tblVhNB8Rpr1ar7zDar7cly9ILr8P6N3Ei7bTEjg==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/fs": "2.9.3",
+        "@parcel/logger": "2.9.3",
+        "@parcel/node-resolver-core": "3.0.3",
+        "@parcel/types": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "@parcel/workers": "2.9.3",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@parcel/packager-css": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.9.3.tgz",
+      "integrity": "sha512-mePiWiYZOULY6e1RdAIJyRoYqXqGci0srOaVZYaP7mnrzvJgA63kaZFFsDiEWghunQpMUuUjM2x/vQVHzxmhKQ==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-html": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.9.3.tgz",
+      "integrity": "sha512-0Ex+O0EaZf9APNERRNGgGto02hFJ6f5RQEvRWBK55WAV1rXeU+kpjC0c0qZvnUaUtXfpWMsEBkevJCwDkUMeMg==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/types": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-js": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.9.3.tgz",
+      "integrity": "sha512-V5xwkoE3zQ3R+WqAWhA1KGQ791FvJeW6KonOlMI1q76Djjgox68hhObqcLu66AmYNhR2R/wUpkP18hP2z8dSFw==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/hash": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.9.3",
+        "globals": "^13.2.0",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-raw": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.9.3.tgz",
+      "integrity": "sha512-oPQTNoYanQ2DdJyL61uPYK2py83rKOT8YVh2QWAx0zsSli6Kiy64U3+xOCYWgDVCrHw9+9NpQMuAdSiFg4cq8g==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-svg": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.9.3.tgz",
+      "integrity": "sha512-p/Ya6UO9DAkaCUFxfFGyeHZDp9YPAlpdnh1OChuwqSFOXFjjeXuoK4KLT+ZRalVBo2Jo8xF70oKMZw4MVvaL7Q==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/types": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "posthtml": "^0.16.4"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/plugin": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.9.3.tgz",
+      "integrity": "sha512-qN85Gqr2GMuxX1dT1mnuO9hOcvlEv1lrYrCxn7CJN2nUhbwcfG+LEvcrCzCOJ6XtIHm+ZBV9h9p7FfoPLvpw+g==",
+      "dependencies": {
+        "@parcel/types": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/profiler": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/profiler/-/profiler-2.9.3.tgz",
+      "integrity": "sha512-pyHc9lw8VZDfgZoeZWZU9J0CVEv1Zw9O5+e0DJPDPHuXJYr72ZAOhbljtU3owWKAeW+++Q2AZWkbUGEOjI/e6g==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/events": "2.9.3",
+        "chrome-trace-event": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/reporter-cli": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.9.3.tgz",
+      "integrity": "sha512-pZiEvQpuXFuQBafMHxkDmwH8CnnK9sWHwa3bSbsnt385aUahtE8dpY0LKt+K1zfB6degKoczN6aWVj9WycQuZQ==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/types": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "chalk": "^4.1.0",
+        "term-size": "^2.2.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/reporter-dev-server": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.9.3.tgz",
+      "integrity": "sha512-s6eboxdLEtRSvG52xi9IiNbcPKC0XMVmvTckieue2EqGDbDcaHQoHmmwkk0rNq0/Z/UxelGcQXoIYC/0xq3ykQ==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/reporter-tracer": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/reporter-tracer/-/reporter-tracer-2.9.3.tgz",
+      "integrity": "sha512-9cXpKWk0m6d6d+4+TlAdOe8XIPaFEIKGWMWG+5SFAQE08u3olet4PSvd49F4+ZZo5ftRE7YI3j6xNbXvJT8KGw==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "chrome-trace-event": "^1.0.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/resolver-default": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.9.3.tgz",
+      "integrity": "sha512-8ESJk1COKvDzkmOnppNXoDamNMlYVIvrKc2RuFPmp8nKVj47R6NwMgvwxEaatyPzvkmyTpq5RvG9I3HFc+r4Cw==",
+      "dependencies": {
+        "@parcel/node-resolver-core": "3.0.3",
+        "@parcel/plugin": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-browser-hmr": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.9.3.tgz",
+      "integrity": "sha512-EgiDIDrVAWpz7bOzWXqVinQkaFjLwT34wsonpXAbuI7f7r00d52vNAQC9AMu+pTijA3gyKoJ+Q4NWPMZf7ACDA==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-js": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.9.3.tgz",
+      "integrity": "sha512-EvIy+qXcKnB5qxHhe96zmJpSAViNVXHfQI5RSdZ2a7CPwORwhTI+zPNT9sb7xb/WwFw/WuTTgzT40b41DceU6Q==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-react-refresh": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.9.3.tgz",
+      "integrity": "sha512-XBgryZQIyCmi6JwEfMUCmINB3l1TpTp9a2iFxmYNpzHlqj4Ve0saKaqWOVRLvC945ZovWIBzcSW2IYqWKGtbAA==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "react-error-overlay": "6.0.9",
+        "react-refresh": "^0.9.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-service-worker": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.9.3.tgz",
+      "integrity": "sha512-qLJLqv1mMdWL7gyh8aKBFFAuEiJkhUUgLKpdn6eSfH/R7kTtb76WnOwqUrhvEI9bZFUM/8Pa1bzJnPpqSOM+Sw==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/source-map": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz",
+      "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==",
+      "dependencies": {
+        "detect-libc": "^1.0.3"
+      },
+      "engines": {
+        "node": "^12.18.3 || >=14"
+      }
+    },
+    "node_modules/@parcel/transformer-babel": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.9.3.tgz",
+      "integrity": "sha512-pURtEsnsp3h6tOBDuzh9wRvVtw4PgIlqwAArIWdrG7iwqOUYv9D8ME4+ePWEu7MQWAp58hv9pTJtqWv4T+Sq8A==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.9.3",
+        "browserslist": "^4.6.6",
+        "json5": "^2.2.0",
+        "nullthrows": "^1.1.1",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-css": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.9.3.tgz",
+      "integrity": "sha512-duWMdbEBBPjg3fQdXF16iWIdThetDZvCs2TpUD7xOlXH6kR0V5BJy8ONFT15u1RCqIV9hSNGaS3v3I9YRNY5zQ==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.9.3",
+        "browserslist": "^4.6.6",
+        "lightningcss": "^1.16.1",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-html": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.9.3.tgz",
+      "integrity": "sha512-0NU4omcHzFXA1seqftAXA2KNZaMByoKaNdXnLgBgtCGDiYvOcL+6xGHgY6pw9LvOh5um10KI5TxSIMILoI7VtA==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/hash": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "posthtml-parser": "^0.10.1",
+        "posthtml-render": "^3.0.0",
+        "semver": "^7.5.2",
+        "srcset": "4"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-image": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.9.3.tgz",
+      "integrity": "sha512-7CEe35RaPadQzLIuxzTtIxnItvOoy46hcbXtOdDt6lmVa4omuOygZYRIya2lsGIP4JHvAaALMb5nt99a1uTwJg==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "@parcel/workers": "2.9.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@parcel/transformer-js": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.9.3.tgz",
+      "integrity": "sha512-Z2MVVg5FYcPOfxlUwxqb5l9yjTMEqE3KI3zq2MBRUme6AV07KxLmCDF23b6glzZlHWQUE8MXzYCTAkOPCcPz+Q==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.9.3",
+        "@parcel/workers": "2.9.3",
+        "@swc/helpers": "^0.5.0",
+        "browserslist": "^4.6.6",
+        "nullthrows": "^1.1.1",
+        "regenerator-runtime": "^0.13.7",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@parcel/transformer-json": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.9.3.tgz",
+      "integrity": "sha512-yNL27dbOLhkkrjaQjiQ7Im9VOxmkfuuSNSmS0rA3gEjVcm07SLKRzWkAaPnyx44Lb6bzyOTWwVrb9aMmxgADpA==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "json5": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-postcss": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.9.3.tgz",
+      "integrity": "sha512-HoDvPqKzhpmvMmHqQhDnt8F1vH61m6plpGiYaYnYv2Om4HHi5ZIq9bO+9QLBnTKfaZ7ndYSefTKOxTYElg7wyw==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/hash": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "clone": "^2.1.1",
+        "nullthrows": "^1.1.1",
+        "postcss-value-parser": "^4.2.0",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-posthtml": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.9.3.tgz",
+      "integrity": "sha512-2fQGgrzRmaqbWf3y2/T6xhqrNjzqMMKksqJzvc8TMfK6f2kg3Ddjv158eaSW2JdkV39aY7tvAOn5f1uzo74BMA==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "posthtml-parser": "^0.10.1",
+        "posthtml-render": "^3.0.0",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-raw": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.9.3.tgz",
+      "integrity": "sha512-oqdPzMC9QzWRbY9J6TZEqltknjno+dY24QWqf8ondmdF2+W+/2mRDu59hhCzQrqUHgTq4FewowRZmSfpzHxwaQ==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-react-refresh-wrap": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.9.3.tgz",
+      "integrity": "sha512-cb9NyU6oJlDblFIlzqIE8AkvRQVGl2IwJNKwD4PdE7Y6sq2okGEPG4hOw3k/Y9JVjM4/2pUORqvjSRhWwd9oVQ==",
+      "dependencies": {
+        "@parcel/plugin": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "react-refresh": "^0.9.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-svg": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.9.3.tgz",
+      "integrity": "sha512-ypmE+dzB09IMCdEAkOsSxq1dEIm2A3h67nAFz4qbfHbwNgXBUuy/jB3ZMwXN/cO0f7SBh/Ap8Jhq6vmGqB5tWw==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/hash": "2.9.3",
+        "@parcel/plugin": "2.9.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "posthtml-parser": "^0.10.1",
+        "posthtml-render": "^3.0.0",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.9.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/types": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.9.3.tgz",
+      "integrity": "sha512-NSNY8sYtRhvF1SqhnIGgGvJocyWt1K8Tnw5cVepm0g38ywtX6mwkBvMkmeehXkII4mSUn+frD9wGsydTunezvA==",
+      "dependencies": {
+        "@parcel/cache": "2.9.3",
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/fs": "2.9.3",
+        "@parcel/package-manager": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/workers": "2.9.3",
+        "utility-types": "^3.10.0"
+      }
+    },
+    "node_modules/@parcel/utils": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.9.3.tgz",
+      "integrity": "sha512-cesanjtj/oLehW8Waq9JFPmAImhoiHX03ihc3JTWkrvJYSbD7wYKCDgPAM3JiRAqvh1LZ6P699uITrYWNoRLUg==",
+      "dependencies": {
+        "@parcel/codeframe": "2.9.3",
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/hash": "2.9.3",
+        "@parcel/logger": "2.9.3",
+        "@parcel/markdown-ansi": "2.9.3",
+        "@parcel/source-map": "^2.1.1",
+        "chalk": "^4.1.0",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.3.0.tgz",
+      "integrity": "sha512-pW7QaFiL11O0BphO+bq3MgqeX/INAk9jgBldVDYjlQPO4VddoZnF22TcF9onMhnLVHuNqBJeRf+Fj7eezi/+rQ==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "detect-libc": "^1.0.3",
+        "is-glob": "^4.0.3",
+        "micromatch": "^4.0.5",
+        "node-addon-api": "^7.0.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher-android-arm64": "2.3.0",
+        "@parcel/watcher-darwin-arm64": "2.3.0",
+        "@parcel/watcher-darwin-x64": "2.3.0",
+        "@parcel/watcher-freebsd-x64": "2.3.0",
+        "@parcel/watcher-linux-arm-glibc": "2.3.0",
+        "@parcel/watcher-linux-arm64-glibc": "2.3.0",
+        "@parcel/watcher-linux-arm64-musl": "2.3.0",
+        "@parcel/watcher-linux-x64-glibc": "2.3.0",
+        "@parcel/watcher-linux-x64-musl": "2.3.0",
+        "@parcel/watcher-win32-arm64": "2.3.0",
+        "@parcel/watcher-win32-ia32": "2.3.0",
+        "@parcel/watcher-win32-x64": "2.3.0"
+      }
+    },
+    "node_modules/@parcel/watcher-android-arm64": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.3.0.tgz",
+      "integrity": "sha512-f4o9eA3dgk0XRT3XhB0UWpWpLnKgrh1IwNJKJ7UJek7eTYccQ8LR7XUWFKqw6aEq5KUNlCcGvSzKqSX/vtWVVA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-arm64": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.3.0.tgz",
+      "integrity": "sha512-mKY+oijI4ahBMc/GygVGvEdOq0L4DxhYgwQqYAz/7yPzuGi79oXrZG52WdpGA1wLBPrYb0T8uBaGFo7I6rvSKw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-x64": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.3.0.tgz",
+      "integrity": "sha512-20oBj8LcEOnLE3mgpy6zuOq8AplPu9NcSSSfyVKgfOhNAc4eF4ob3ldj0xWjGGbOF7Dcy1Tvm6ytvgdjlfUeow==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-freebsd-x64": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.3.0.tgz",
+      "integrity": "sha512-7LftKlaHunueAEiojhCn+Ef2CTXWsLgTl4hq0pkhkTBFI3ssj2bJXmH2L67mKpiAD5dz66JYk4zS66qzdnIOgw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-glibc": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.3.0.tgz",
+      "integrity": "sha512-1apPw5cD2xBv1XIHPUlq0cO6iAaEUQ3BcY0ysSyD9Kuyw4MoWm1DV+W9mneWI+1g6OeP6dhikiFE6BlU+AToTQ==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-glibc": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.3.0.tgz",
+      "integrity": "sha512-mQ0gBSQEiq1k/MMkgcSB0Ic47UORZBmWoAWlMrTW6nbAGoLZP+h7AtUM7H3oDu34TBFFvjy4JCGP43JlylkTQA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-musl": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.3.0.tgz",
+      "integrity": "sha512-LXZAExpepJew0Gp8ZkJ+xDZaTQjLHv48h0p0Vw2VMFQ8A+RKrAvpFuPVCVwKJCr5SE+zvaG+Etg56qXvTDIedw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-glibc": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.3.0.tgz",
+      "integrity": "sha512-P7Wo91lKSeSgMTtG7CnBS6WrA5otr1K7shhSjKHNePVmfBHDoAOHYRXgUmhiNfbcGk0uMCHVcdbfxtuiZCHVow==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-musl": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.3.0.tgz",
+      "integrity": "sha512-+kiRE1JIq8QdxzwoYY+wzBs9YbJ34guBweTK8nlzLKimn5EQ2b2FSC+tAOpq302BuIMjyuUGvBiUhEcLIGMQ5g==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-arm64": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.3.0.tgz",
+      "integrity": "sha512-35gXCnaz1AqIXpG42evcoP2+sNL62gZTMZne3IackM+6QlfMcJLy3DrjuL6Iks7Czpd3j4xRBzez3ADCj1l7Aw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-ia32": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.3.0.tgz",
+      "integrity": "sha512-FJS/IBQHhRpZ6PiCjFt1UAcPr0YmCLHRbTc00IBTrelEjlmmgIVLeOx4MSXzx2HFEy5Jo5YdhGpxCuqCyDJ5ow==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-x64": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.3.0.tgz",
+      "integrity": "sha512-dLx+0XRdMnVI62kU3wbXvbIRhLck4aE28bIGKbRGS7BJNt54IIj9+c/Dkqb+7DJEbHUZAX1bwaoM8PqVlHJmCA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/workers": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.9.3.tgz",
+      "integrity": "sha512-zRrDuZJzTevrrwElYosFztgldhqW6G9q5zOeQXfVQFkkEJCNfg36ixeiofKRU8uu2x+j+T6216mhMNB6HiuY+w==",
+      "dependencies": {
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/logger": "2.9.3",
+        "@parcel/profiler": "2.9.3",
+        "@parcel/types": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.9.3"
+      }
+    },
+    "node_modules/@swc/core": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.82.tgz",
+      "integrity": "sha512-jpC1a18HMH67018Ij2jh+hT7JBFu7ZKcQVfrZ8K6JuEY+kjXmbea07P9MbQUZbAe0FB+xi3CqEVCP73MebodJQ==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@swc/types": "^0.1.4"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/swc"
+      },
+      "optionalDependencies": {
+        "@swc/core-darwin-arm64": "1.3.82",
+        "@swc/core-darwin-x64": "1.3.82",
+        "@swc/core-linux-arm-gnueabihf": "1.3.82",
+        "@swc/core-linux-arm64-gnu": "1.3.82",
+        "@swc/core-linux-arm64-musl": "1.3.82",
+        "@swc/core-linux-x64-gnu": "1.3.82",
+        "@swc/core-linux-x64-musl": "1.3.82",
+        "@swc/core-win32-arm64-msvc": "1.3.82",
+        "@swc/core-win32-ia32-msvc": "1.3.82",
+        "@swc/core-win32-x64-msvc": "1.3.82"
+      },
+      "peerDependencies": {
+        "@swc/helpers": "^0.5.0"
+      },
+      "peerDependenciesMeta": {
+        "@swc/helpers": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@swc/core-darwin-arm64": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.82.tgz",
+      "integrity": "sha512-JfsyDW34gVKD3uE0OUpUqYvAD3yseEaicnFP6pB292THtLJb0IKBBnK50vV/RzEJtc1bR3g1kNfxo2PeurZTrA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-darwin-x64": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.82.tgz",
+      "integrity": "sha512-ogQWgNMq7qTpITjcP3dnzkFNj7bh6SwMr859GvtOTrE75H7L7jDWxESfH4f8foB/LGxBKiDNmxKhitCuAsZK4A==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-arm-gnueabihf": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.82.tgz",
+      "integrity": "sha512-7TMXG1lXlNhD0kUiEqs+YlGV4irAdBa2quuy+XI3oJf2fBK6dQfEq4xBy65B3khrorzQS3O0oDGQ+cmdpHExHA==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-arm64-gnu": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.82.tgz",
+      "integrity": "sha512-26JkOujbzcItPAmIbD5vHJxQVy5ihcSu3YHTKwope1h28sApZdtE7S3e2G3gsZRTIdsCQkXUtAQeqHxGWWR3pw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-arm64-musl": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.82.tgz",
+      "integrity": "sha512-8Izj9tuuMpoc3cqiPBRtwqpO1BZ/+sfZVsEhLxrbOFlcSb8LnKyMle1g3JMMUwI4EU75RGVIzZMn8A6GOKdJbA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-x64-gnu": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.82.tgz",
+      "integrity": "sha512-0GSrIBScQwTaPv46T2qB7XnDYxndRCpwH4HMjh6FN+I+lfPUhTSJKW8AonqrqT1TbpFIgvzQs7EnTsD7AnSCow==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-x64-musl": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.82.tgz",
+      "integrity": "sha512-KJUnaaepDKNzrEbwz4jv0iC3/t9x0NSoe06fnkAlhh2+NFKWKKJhVCOBTrpds8n7eylBDIXUlK34XQafjVMUdg==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-win32-arm64-msvc": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.82.tgz",
+      "integrity": "sha512-TR3MHKhDYIyGyFcyl2d/p1ftceXcubAhX5wRSOdtOyr5+K/v3jbyCCqN7bbqO5o43wQVCwwR/drHleYyDZvg8Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-win32-ia32-msvc": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.82.tgz",
+      "integrity": "sha512-ZX4HzVVt6hs84YUg70UvyBJnBOIspmQQM0iXSzBvOikk3zRoN7BnDwQH4GScvevCEBuou60+i4I6d5kHLOfh8Q==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-win32-x64-msvc": {
+      "version": "1.3.82",
+      "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.82.tgz",
+      "integrity": "sha512-4mJMnex21kbQoaHeAmHnVwQN9/XAfPszJ6n9HI7SVH+aAHnbBIR0M59/b50/CJMjTj5niUGk7EwQ3nhVNOG32g==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/helpers": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
+      "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==",
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@swc/types": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.4.tgz",
+      "integrity": "sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg=="
+    },
+    "node_modules/@trysound/sax": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+      "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "20.5.9",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz",
+      "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ=="
+    },
+    "node_modules/@types/node-fetch": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz",
+      "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==",
+      "dependencies": {
+        "@types/node": "*",
+        "form-data": "^3.0.0"
+      }
+    },
+    "node_modules/@types/node-fetch/node_modules/form-data": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+      "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/@types/tunnel": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
+      "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/abortcontroller-polyfill": {
+      "version": "1.7.5",
+      "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz",
+      "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ=="
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/base-x": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
+      "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.21.10",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz",
+      "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001517",
+        "electron-to-chromium": "^1.4.477",
+        "node-releases": "^2.0.13",
+        "update-browserslist-db": "^1.0.11"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001525",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz",
+      "integrity": "sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ]
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chrome-trace-event": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+      "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/cosmiconfig": {
+      "version": "8.3.4",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.4.tgz",
+      "integrity": "sha512-SF+2P8+o/PTV05rgsAjDzL4OFdVXAulSfC/L19VaeVT7+tpOOSscCt2QLxDZ+CLxF2WOiq6y1K5asvs8qUJT/Q==",
+      "dependencies": {
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0",
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/d-fischer"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/css-select": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+      "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.1.0",
+        "domhandler": "^5.0.2",
+        "domutils": "^3.0.1",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-select/node_modules/dom-serializer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+      "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.2",
+        "entities": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/css-select/node_modules/domhandler": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+      "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "domelementtype": "^2.3.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/css-select/node_modules/domutils": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+      "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "dom-serializer": "^2.0.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/css-select/node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "optional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/css-tree": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+      "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "mdn-data": "2.0.30",
+        "source-map-js": "^1.0.1"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/csso": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+      "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "css-tree": "~2.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/csso/node_modules/css-tree": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+      "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "mdn-data": "2.0.28",
+        "source-map-js": "^1.0.1"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+        "npm": ">=7.0.0"
+      }
+    },
+    "node_modules/csso/node_modules/mdn-data": {
+      "version": "2.0.28",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+      "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+      "optional": true,
+      "peer": true
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+      "bin": {
+        "detect-libc": "bin/detect-libc.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/dom-serializer/node_modules/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ]
+    },
+    "node_modules/domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz",
+      "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+      "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.4.508",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz",
+      "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg=="
+    },
+    "node_modules/entities": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+      "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/get-port": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz",
+      "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/globals": {
+      "version": "13.21.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+      "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/htmlnano": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.4.tgz",
+      "integrity": "sha512-WGCkyGFwjKW1GeCBsPYacMvaMnZtFJ0zIRnC2NCddkA+IOEhTqskXrS7lep+3yYZw/nQ3dW1UAX4yA/GJyR8BA==",
+      "dependencies": {
+        "cosmiconfig": "^8.0.0",
+        "posthtml": "^0.16.5",
+        "timsort": "^0.3.0"
+      },
+      "peerDependencies": {
+        "cssnano": "^6.0.0",
+        "postcss": "^8.3.11",
+        "purgecss": "^5.0.0",
+        "relateurl": "^0.2.7",
+        "srcset": "4.0.0",
+        "svgo": "^3.0.2",
+        "terser": "^5.10.0",
+        "uncss": "^0.17.3"
+      },
+      "peerDependenciesMeta": {
+        "cssnano": {
+          "optional": true
+        },
+        "postcss": {
+          "optional": true
+        },
+        "purgecss": {
+          "optional": true
+        },
+        "relateurl": {
+          "optional": true
+        },
+        "srcset": {
+          "optional": true
+        },
+        "svgo": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "uncss": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/htmlparser2": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
+      "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.2",
+        "domutils": "^2.8.0",
+        "entities": "^3.0.1"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-json": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz",
+      "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA=="
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/lightningcss": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.21.7.tgz",
+      "integrity": "sha512-xITZyh5sLFwRPYUSw15T00Rm7gcQ1qOPuQwNOcvHsTm6nLWTQ723w7zl42wrC5t+xtdg6FPmnXHml1nZxxvp1w==",
+      "dependencies": {
+        "detect-libc": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "lightningcss-darwin-arm64": "1.21.7",
+        "lightningcss-darwin-x64": "1.21.7",
+        "lightningcss-freebsd-x64": "1.21.7",
+        "lightningcss-linux-arm-gnueabihf": "1.21.7",
+        "lightningcss-linux-arm64-gnu": "1.21.7",
+        "lightningcss-linux-arm64-musl": "1.21.7",
+        "lightningcss-linux-x64-gnu": "1.21.7",
+        "lightningcss-linux-x64-musl": "1.21.7",
+        "lightningcss-win32-x64-msvc": "1.21.7"
+      }
+    },
+    "node_modules/lightningcss-darwin-arm64": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.21.7.tgz",
+      "integrity": "sha512-tt7hIsFio9jZofTVHtCACz6rB6c9RyABMXfA9A/VcKOjS3sq+koX/QkRJWY06utwOImbJIXBC5hbg9t3RkPUAQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-darwin-x64": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.21.7.tgz",
+      "integrity": "sha512-F4gS4bf7eWekfPT+TxJNm/pF+QRgZiTrTkQH6cw4/UWfdeZISfuhD5El2dm16giFnY0K5ylIwO+ZusgYNkGSXA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-freebsd-x64": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.21.7.tgz",
+      "integrity": "sha512-RMfNzJWXCSfPnL55fcLWEAadcY6QUFT0S8NceNKYzp1KiCZtkJIy6RQ5SaVxPzRqd3iMsahUf5sfnG8N1UQSNQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm-gnueabihf": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.21.7.tgz",
+      "integrity": "sha512-biSRUDZNx7vubWP1jArw/qqfZKPGpkV/qzunasZzxmqijbZ43sW9faDQYxWNcxPWljJJdF/qs6qcurYFovWtrQ==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-gnu": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.21.7.tgz",
+      "integrity": "sha512-PENY8QekqL9TG3AY/A7rkUBb5ymefGxea7Oe7+x7Hbw4Bz4Hpj5cec5OoMypMqFbURPmpi0fTWx4vSWUPzpDcA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-musl": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.21.7.tgz",
+      "integrity": "sha512-pfOipKvA/0X1OjRaZt3870vnV9UGBSjayIqHh0fGx/+aRz3O0MVFHE/60P2UWXpM3YGJEw/hMWtNkrFwqOge8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-gnu": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.21.7.tgz",
+      "integrity": "sha512-dgcsis4TAA7s0ia4f31QHX+G4PWPwxk+wJaEQLaV0NdJs09O5hHoA8DpLEr8nrvc/tsRTyVNBP1rDtgzySjpXg==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-musl": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.21.7.tgz",
+      "integrity": "sha512-A+9dXpxld3p4Cd6fxev2eqEvaauYtrgNpXV3t7ioCJy30Oj9nYiNGwiGusM+4MJVcEpUPGUGiuAqY4sWilRDwA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-win32-x64-msvc": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.21.7.tgz",
+      "integrity": "sha512-07/8vogEq+C/mF99pdMhh/f19/xreq8N9Ca6AWeVHZIdODyF/pt6KdKSCWDZWIn+3CUxI8gCJWuUWyOc3xymvw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+    },
+    "node_modules/lmdb": {
+      "version": "2.7.11",
+      "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.7.11.tgz",
+      "integrity": "sha512-x9bD4hVp7PFLUoELL8RglbNXhAMt5CYhkmss+CEau9KlNoilsTzNi9QDsPZb3KMpOGZXG6jmXhW3bBxE2XVztw==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "msgpackr": "1.8.5",
+        "node-addon-api": "^4.3.0",
+        "node-gyp-build-optional-packages": "5.0.6",
+        "ordered-binary": "^1.4.0",
+        "weak-lru-cache": "^1.2.2"
+      },
+      "bin": {
+        "download-lmdb-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@lmdb/lmdb-darwin-arm64": "2.7.11",
+        "@lmdb/lmdb-darwin-x64": "2.7.11",
+        "@lmdb/lmdb-linux-arm": "2.7.11",
+        "@lmdb/lmdb-linux-arm64": "2.7.11",
+        "@lmdb/lmdb-linux-x64": "2.7.11",
+        "@lmdb/lmdb-win32-x64": "2.7.11"
+      }
+    },
+    "node_modules/lmdb/node_modules/msgpackr": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.5.tgz",
+      "integrity": "sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg==",
+      "optionalDependencies": {
+        "msgpackr-extract": "^3.0.1"
+      }
+    },
+    "node_modules/lmdb/node_modules/node-addon-api": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
+      "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
+    },
+    "node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/mdn-data": {
+      "version": "2.0.30",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+      "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+      "optional": true,
+      "peer": true
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dependencies": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/msgpackr": {
+      "version": "1.9.8",
+      "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.9.8.tgz",
+      "integrity": "sha512-dQvfSMSIQ9kXXQTlJFDq+f7J3RrmydhI6Tn23lFy7BItp7zDR3nH70CHk2QIfs2copLSaKRv/PPjMbNSTFu2hA==",
+      "optionalDependencies": {
+        "msgpackr-extract": "^3.0.2"
+      }
+    },
+    "node_modules/msgpackr-extract": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz",
+      "integrity": "sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==",
+      "hasInstallScript": true,
+      "optional": true,
+      "dependencies": {
+        "node-gyp-build-optional-packages": "5.0.7"
+      },
+      "bin": {
+        "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.2"
+      }
+    },
+    "node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz",
+      "integrity": "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==",
+      "optional": true,
+      "bin": {
+        "node-gyp-build-optional-packages": "bin.js",
+        "node-gyp-build-optional-packages-optional": "optional.js",
+        "node-gyp-build-optional-packages-test": "build-test.js"
+      }
+    },
+    "node_modules/node-addon-api": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz",
+      "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA=="
+    },
+    "node_modules/node-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-gyp-build-optional-packages": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.6.tgz",
+      "integrity": "sha512-2ZJErHG4du9G3/8IWl/l9Bp5BBFy63rno5GVmjQijvTuUZKsl6g8RB4KH/x3NLcV5ZBb4GsXmAuTYr6dRml3Gw==",
+      "bin": {
+        "node-gyp-build-optional-packages": "bin.js",
+        "node-gyp-build-optional-packages-optional": "optional.js",
+        "node-gyp-build-optional-packages-test": "build-test.js"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.13",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
+      "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/nullthrows": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
+      "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="
+    },
+    "node_modules/ordered-binary": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.4.1.tgz",
+      "integrity": "sha512-9LtiGlPy982CsgxZvJGNNp2/NnrgEr6EAyN3iIEP3/8vd3YLgAZQHbQ75ZrkfBRGrNg37Dk3U6tuVb+B4Xfslg=="
+    },
+    "node_modules/parcel": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.9.3.tgz",
+      "integrity": "sha512-2GTVocFkwblV/TIg9AmT7TI2fO4xdWkyN8aFUEVtiVNWt96GTR3FgQyHFValfCbcj1k9Xf962Ws2hYXYUr9k1Q==",
+      "dependencies": {
+        "@parcel/config-default": "2.9.3",
+        "@parcel/core": "2.9.3",
+        "@parcel/diagnostic": "2.9.3",
+        "@parcel/events": "2.9.3",
+        "@parcel/fs": "2.9.3",
+        "@parcel/logger": "2.9.3",
+        "@parcel/package-manager": "2.9.3",
+        "@parcel/reporter-cli": "2.9.3",
+        "@parcel/reporter-dev-server": "2.9.3",
+        "@parcel/reporter-tracer": "2.9.3",
+        "@parcel/utils": "2.9.3",
+        "chalk": "^4.1.0",
+        "commander": "^7.0.0",
+        "get-port": "^4.2.0"
+      },
+      "bin": {
+        "parcel": "lib/bin.js"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/postcss-value-parser": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+    },
+    "node_modules/posthtml": {
+      "version": "0.16.6",
+      "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz",
+      "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==",
+      "dependencies": {
+        "posthtml-parser": "^0.11.0",
+        "posthtml-render": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/posthtml-parser": {
+      "version": "0.10.2",
+      "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz",
+      "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==",
+      "dependencies": {
+        "htmlparser2": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/posthtml-render": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz",
+      "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==",
+      "dependencies": {
+        "is-json": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/posthtml/node_modules/posthtml-parser": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz",
+      "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==",
+      "dependencies": {
+        "htmlparser2": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/react-error-overlay": {
+      "version": "6.0.9",
+      "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
+      "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
+    },
+    "node_modules/react-refresh": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz",
+      "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+    },
+    "node_modules/semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "optional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/srcset": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz",
+      "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/stable": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+      "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility"
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/svgo": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
+      "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "@trysound/sax": "0.2.0",
+        "commander": "^7.2.0",
+        "css-select": "^5.1.0",
+        "css-tree": "^2.2.1",
+        "csso": "^5.0.5",
+        "picocolors": "^1.0.0"
+      },
+      "bin": {
+        "svgo": "bin/svgo"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/svgo"
+      }
+    },
+    "node_modules/term-size": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
+      "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/timsort": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
+      "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A=="
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+    },
+    "node_modules/tslib": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+      "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+    },
+    "node_modules/tunnel": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+      "engines": {
+        "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
+      "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.1.1",
+        "picocolors": "^1.0.0"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/utility-types": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
+      "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/weak-lru-cache": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
+      "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw=="
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/xml2js": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+      "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
+      "dependencies": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/xxhash-wasm": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz",
+      "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA=="
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+    }
+  }
+}
diff --git a/app/doc_uploader/client/package.json b/app/doc_uploader/client/package.json
new file mode 100644
index 0000000000..c7e0bc19e8
--- /dev/null
+++ b/app/doc_uploader/client/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "static",
+  "version": "1.0.0",
+  "description": "",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "@azure/storage-blob": "^12.15.0",
+    "parcel": "^2.9.3"
+  },
+  "devDependencies": {
+    "buffer": "^6.0.3"
+  }
+}
diff --git a/app/doc_uploader/main.py b/app/doc_uploader/main.py
new file mode 100644
index 0000000000..f9585b32e8
--- /dev/null
+++ b/app/doc_uploader/main.py
@@ -0,0 +1,111 @@
+import datetime
+from azure.storage.blob import BlobClient, generate_blob_sas, BlobSasPermissions, BlobServiceClient
+from azure.core.utils import parse_connection_string
+from fastapi import FastAPI
+from azure.identity import DefaultAzureCredential
+from azure.mgmt.appcontainers import ContainerAppsAPIClient
+from azure.mgmt.appcontainers.models import JobExecutionTemplate, JobExecutionContainer
+import os
+from azure.identity import DefaultAzureCredential
+
+from fastapi.staticfiles import StaticFiles
+
+
+app = FastAPI()
+
+
+@app.get("/sas")
+async def sas(filename: str):
+    storage_account_name = os.environ["AZURE_STORAGE_ACCOUNT"]
+    account_url = f"https://{storage_account_name}.blob.core.windows.net"
+    credential = DefaultAzureCredential()
+
+    current_time = datetime.datetime.now(datetime.timezone.utc)
+    start_time = current_time - datetime.timedelta(minutes=1)
+    expiry_time = current_time + datetime.timedelta(days=1)
+
+    blob_service_client = BlobServiceClient(account_url=account_url, credential=credential)
+    account_key = blob_service_client.get_user_delegation_key(key_start_time=start_time, key_expiry_time=expiry_time)
+
+    blob_name = filename
+    container_name = "uploads"
+
+    sas_token = generate_blob_sas(
+        account_name=storage_account_name,
+        container_name=container_name,
+        blob_name=blob_name,
+        account_key=account_key,
+        permission=BlobSasPermissions(write=True),
+        expiry=expiry_time,
+        start=start_time
+    )
+    
+    return {"sasUrl": f"{account_url}/{container_name}/{blob_name}?{sas_token}"}
+
+
+@app.post("/startjob")
+async def start_job(filename: str):
+    storage_account_name = os.environ["AZURE_STORAGE_ACCOUNT"]
+    account_url = f"https://{storage_account_name}.blob.core.windows.net"
+    credential = DefaultAzureCredential()
+
+    current_time = datetime.datetime.now(datetime.timezone.utc)
+    start_time = current_time - datetime.timedelta(minutes=1)
+    expiry_time = current_time + datetime.timedelta(days=1)
+
+    blob_service_client = BlobServiceClient(account_url=account_url, credential=credential)
+    account_key = blob_service_client.get_user_delegation_key(key_start_time=start_time, key_expiry_time=expiry_time)
+
+    blob_name = filename
+    container_name = "uploads"
+
+    sas_token = generate_blob_sas(
+        account_name=storage_account_name,
+        container_name=container_name,
+        blob_name=blob_name,
+        account_key=account_key,
+        permission=BlobSasPermissions(read=True),
+        expiry=expiry_time,
+        start=start_time
+    )
+    
+    sas_url = f"{account_url}/{container_name}/{blob_name}?{sas_token}"
+
+    # generate a log file name based on the timestamp
+    log_file_name = f"{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}.log"
+
+    sub_id = os.environ["AZURE_SUBSCRIPTION_ID"]
+    client = ContainerAppsAPIClient(credential=DefaultAzureCredential(), subscription_id=sub_id)
+    template = client.jobs.get(os.environ["AZURE_RESOURCE_GROUP"], 'job-prepdocs').template
+    env_vars = template.containers[0].env
+    # find the env var named "BLOB_URL" and set its value to the filename
+    for env_var in env_vars:
+        if env_var.name == "BLOB_URL":
+            env_var.value = sas_url
+            print(f"Set BLOB_URL to {sas_url}")
+        if env_var.name == "LOG_FILE_NAME":
+            env_var.value = log_file_name
+            print(f"Set LOG_FILE_NAME to {log_file_name}")
+    
+    result = client.jobs.begin_start(os.environ["AZURE_RESOURCE_GROUP"], 'job-prepdocs', template).result()
+    print(result)
+
+    logs_sas_token = generate_blob_sas(
+        account_name=storage_account_name,
+        container_name="prepdocslogs",
+        blob_name=log_file_name,
+        account_key=account_key,
+        permission=BlobSasPermissions(read=True),
+        expiry=expiry_time,
+        start=start_time
+    )
+
+    logs_sas_url = f"{account_url}/prepdocslogs/{log_file_name}?{logs_sas_token}"
+    return {
+        "result": result,
+        "logFileUrl": logs_sas_url
+    }
+
+
+
+app.mount("/", StaticFiles(directory="dist", html=True), name="dist")
\ No newline at end of file
diff --git a/app/doc_uploader/requirements.txt b/app/doc_uploader/requirements.txt
new file mode 100644
index 0000000000..1a7bddb263
--- /dev/null
+++ b/app/doc_uploader/requirements.txt
@@ -0,0 +1,7 @@
+fastapi==0.101.1
+uvicorn==0.23.2
+gunicorn==21.2.0
+azure-storage-blob==12.17.0
+azure-mgmt-storage==21.1.0
+azure-identity==1.14.0
+azure-mgmt-appcontainers==3.0.0
diff --git a/azure.yaml b/azure.yaml
index 6c323bb8c7..8f5d99c2d0 100644
--- a/azure.yaml
+++ b/azure.yaml
@@ -4,10 +4,10 @@ name: azure-search-openai-demo
 metadata:
   template: azure-search-openai-demo@0.0.2-beta
 services:
-  backend:
+  web:
     project: ./app/backend
     language: py
-    host: appservice
+    host: containerapp
     hooks:
       prepackage:
         windows:
diff --git a/infra/core/host/appservice-appsettings.bicep b/infra/core/host/appservice-appsettings.bicep
new file mode 100644
index 0000000000..2286e1f5b4
--- /dev/null
+++ b/infra/core/host/appservice-appsettings.bicep
@@ -0,0 +1,15 @@
+@description('The name of the app service resource within the current resource group scope')
+param name string
+
+@description('The app settings to be applied to the app service')
+param appSettings object
+
+resource appService 'Microsoft.Web/sites@2022-03-01' existing = {
+  name: name
+}
+
+resource settings 'Microsoft.Web/sites/config@2022-03-01' = {
+  name: 'appsettings'
+  parent: appService
+  properties: appSettings
+}
diff --git a/infra/core/host/appservice.bicep b/infra/core/host/appservice.bicep
index c1de148a2d..f61b4a03ae 100644
--- a/infra/core/host/appservice.bicep
+++ b/infra/core/host/appservice.bicep
@@ -46,10 +46,10 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = {
       linuxFxVersion: linuxFxVersion
       alwaysOn: alwaysOn
       ftpsState: ftpsState
+      minTlsVersion: '1.2'
       appCommandLine: appCommandLine
       numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null
       minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null
-      minTlsVersion: '1.2'
       use32BitWorkerProcess: use32BitWorkerProcess
       functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null
       healthCheckPath: healthCheckPath
@@ -63,18 +63,6 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = {
 
   identity: { type: managedIdentity ? 'SystemAssigned' : 'None' }
 
-  resource configAppSettings 'config' = {
-    name: 'appsettings'
-    properties: union(appSettings,
-      {
-        SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment)
-        ENABLE_ORYX_BUILD: string(enableOryxBuild)
-      },
-      runtimeName == 'python' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {},
-      !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {},
-      !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {})
-  }
-
   resource configLogs 'config' = {
     name: 'logs'
     properties: {
@@ -83,9 +71,36 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = {
       failedRequestsTracing: { enabled: true }
       httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } }
     }
-    dependsOn: [
-      configAppSettings
-    ]
+  }
+
+  resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = {
+    name: 'ftp'
+    location: location
+    properties: {
+      allow: false
+    }
+  }
+
+  resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = {
+    name: 'scm'
+    location: location
+    properties: {
+      allow: false
+    }
+  }
+}
+
+module config 'appservice-appsettings.bicep' = if (!empty(appSettings)) {
+  name: '${name}-appSettings'
+  params: {
+    name: appService.name
+    appSettings: union(appSettings,
+      {
+        SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment)
+        ENABLE_ORYX_BUILD: string(enableOryxBuild)
+      },
+      !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {},
+      !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {})
   }
 }
 
diff --git a/infra/core/host/container-app-upsert.bicep b/infra/core/host/container-app-upsert.bicep
new file mode 100644
index 0000000000..99466871a9
--- /dev/null
+++ b/infra/core/host/container-app-upsert.bicep
@@ -0,0 +1,81 @@
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+param containerAppsEnvironmentName string
+param containerName string = 'main'
+param containerRegistryName string
+
+@description('Minimum number of replicas to run')
+@minValue(1)
+param containerMinReplicas int = 1
+@description('Maximum number of replicas to run')
+@minValue(1)
+param containerMaxReplicas int = 10
+
+param secrets array = []
+param env array = []
+param external bool = true
+param targetPort int = 80
+param exists bool
+
+@description('User assigned identity name')
+param identityName string
+
+@description('Enabled Ingress for container app')
+param ingressEnabled bool = true
+
+// Dapr Options
+@description('Enable Dapr')
+param daprEnabled bool = false
+@description('Dapr app ID')
+param daprAppId string = containerName
+@allowed([ 'http', 'grpc' ])
+@description('Protocol used by Dapr to connect to the app, e.g. http or grpc')
+param daprAppProtocol string = 'http'
+
+// Service options
+@description('PostgreSQL service ID')
+param postgresServiceId string = ''
+
+@description('CPU cores allocated to a single container instance, e.g. 0.5')
+param containerCpuCoreCount string = '2'
+
+@description('Memory allocated to a single container instance, e.g. 1Gi')
+param containerMemory string = '4.0Gi'
+
+resource existingApp 'Microsoft.App/containerApps@2022-03-01' existing = if (exists) {
+  name: name
+}
+
+module app 'container-app.bicep' = {
+  name: '${deployment().name}-update'
+  params: {
+    name: name
+    location: location
+    tags: tags
+    identityName: identityName
+    ingressEnabled: ingressEnabled
+    containerName: containerName
+    containerAppsEnvironmentName: containerAppsEnvironmentName
+    containerRegistryName: containerRegistryName
+    containerCpuCoreCount: containerCpuCoreCount
+    containerMemory: containerMemory
+    containerMinReplicas: containerMinReplicas
+    containerMaxReplicas: containerMaxReplicas
+    daprEnabled: daprEnabled
+    daprAppId: daprAppId
+    daprAppProtocol: daprAppProtocol
+    postgresServiceId: postgresServiceId
+    secrets: secrets
+    external: external
+    env: env
+    imageName: exists ? existingApp.properties.template.containers[0].image : ''
+    targetPort: targetPort
+  }
+}
+
+output defaultDomain string = app.outputs.defaultDomain
+output imageName string = app.outputs.imageName
+output name string = app.outputs.name
+output uri string = app.outputs.uri
diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep
new file mode 100644
index 0000000000..74654d9fb6
--- /dev/null
+++ b/infra/core/host/container-app.bicep
@@ -0,0 +1,133 @@
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+param containerAppsEnvironmentName string
+param containerName string = 'main'
+param containerRegistryName string
+
+@description('Minimum number of replicas to run')
+@minValue(1)
+param containerMinReplicas int = 1
+@description('Maximum number of replicas to run')
+@minValue(1)
+param containerMaxReplicas int = 10
+
+param secrets array = []
+param env array = []
+param external bool = true
+param imageName string
+param targetPort int = 80
+
+@description('User assigned identity name')
+param identityName string
+
+@description('Enabled Ingress for container app')
+param ingressEnabled bool = true
+
+// Dapr Options
+@description('Enable Dapr')
+param daprEnabled bool = false
+@description('Dapr app ID')
+param daprAppId string = containerName
+@allowed([ 'http', 'grpc' ])
+@description('Protocol used by Dapr to connect to the app, e.g. http or grpc')
+param daprAppProtocol string = 'http'
+
+// Service options
+@description('PostgreSQL service ID')
+param postgresServiceId string = ''
+
+@description('CPU cores allocated to a single container instance, e.g. 0.5')
+param containerCpuCoreCount string = '2'
+
+@description('Memory allocated to a single container instance, e.g. 1Gi')
+param containerMemory string = '4.0Gi'
+
+resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
+  name: identityName
+}
+
+module containerRegistryAccess '../security/registry-access.bicep' = {
+  name: '${deployment().name}-registry-access'
+  params: {
+    containerRegistryName: containerRegistryName
+    principalId: userIdentity.properties.principalId
+  }
+}
+
+resource app 'Microsoft.App/containerApps@2023-04-01-preview' = {
+  name: name
+  location: location
+  tags: tags
+  // It is critical that the identity is granted ACR pull access before the app is created
+  // otherwise the container app will throw a provision error
+  // This also forces us to use an user assigned managed identity since there would no way to
+  // provide the system assigned identity with the ACR pull access before the app is created
+  dependsOn: [ containerRegistryAccess ]
+  identity: {
+    type: 'UserAssigned'
+    userAssignedIdentities: { '${userIdentity.id}': {} }
+  }
+  properties: {
+    managedEnvironmentId: containerAppsEnvironment.id
+    configuration: {
+      activeRevisionsMode: 'single'
+      ingress: ingressEnabled ? {
+        external: external
+        targetPort: targetPort
+        transport: 'auto'
+      } : null
+      dapr: daprEnabled ? {
+        enabled: true
+        appId: daprAppId
+        appProtocol: daprAppProtocol
+        appPort: ingressEnabled ? targetPort : 0
+      } : { enabled: false }
+      secrets: secrets
+      registries: [
+        {
+          server: '${containerRegistry.name}.azurecr.io'
+          identity: userIdentity.id
+        }
+      ]
+    }
+    template: {
+      serviceBinds: !empty(postgresServiceId) ? [
+        {
+          serviceId: postgresServiceId
+          name: 'postgres'
+        }
+      ] : []
+      containers: [
+        {
+          image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+          name: containerName
+          env: env
+          resources: {
+            cpu: json(containerCpuCoreCount)
+            memory: containerMemory
+          }
+        }
+      ]
+      scale: {
+        minReplicas: containerMinReplicas
+        maxReplicas: containerMaxReplicas
+      }
+    }
+  }
+}
+
+resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = {
+  name: containerAppsEnvironmentName
+}
+
+// 2022-02-01-preview needed for anonymousPullEnabled
+resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = {
+  name: containerRegistryName
+}
+
+output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
+output imageName string = imageName
+output name string = app.name
+output uri string = 'https://${app.properties.configuration.ingress.fqdn}'
diff --git a/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep
new file mode 100644
index 0000000000..aad73efd87
--- /dev/null
+++ b/infra/core/host/container-apps-environment.bicep
@@ -0,0 +1,41 @@
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+param daprEnabled bool = false
+param logAnalyticsWorkspaceName string
+param applicationInsightsName string = ''
+
+resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+  name: name
+  location: location
+  tags: tags
+  properties: {
+    appLogsConfiguration: {
+      destination: 'log-analytics'
+      logAnalyticsConfiguration: {
+        customerId: logAnalyticsWorkspace.properties.customerId
+        sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
+      }
+    }
+    daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : ''
+    workloadProfiles: [
+      {
+        name: 'Consumption'
+        workloadProfileType: 'Consumption'
+      }
+    ]
+  }
+}
+
+resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
+  name: logAnalyticsWorkspaceName
+}
+
+resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)){
+  name: applicationInsightsName
+}
+
+output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
+output name string = containerAppsEnvironment.name
+output id string = containerAppsEnvironment.id
diff --git a/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep
new file mode 100644
index 0000000000..087a4ae838
--- /dev/null
+++ b/infra/core/host/container-apps.bicep
@@ -0,0 +1,34 @@
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+param containerAppsEnvironmentName string
+param containerRegistryName string
+param logAnalyticsWorkspaceName string
+param applicationInsightsName string = ''
+
+module containerAppsEnvironment 'container-apps-environment.bicep' = {
+  name: '${name}-container-apps-environment'
+  params: {
+    name: containerAppsEnvironmentName
+    location: location
+    tags: tags
+    logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
+    applicationInsightsName: applicationInsightsName
+  }
+}
+
+module containerRegistry 'container-registry.bicep' = {
+  name: '${name}-container-registry'
+  params: {
+    name: containerRegistryName
+    location: location
+    tags: tags
+  }
+}
+
+output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain
+output environmentName string = containerAppsEnvironment.outputs.name
+output environmentId string = containerAppsEnvironment.outputs.id
+output registryLoginServer string = containerRegistry.outputs.loginServer
+output registryName string = containerRegistry.outputs.name
diff --git a/infra/core/host/container-registry.bicep b/infra/core/host/container-registry.bicep
new file mode 100644
index 0000000000..c0ba201b50
--- /dev/null
+++ b/infra/core/host/container-registry.bicep
@@ -0,0 +1,67 @@
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+param adminUserEnabled bool = true
+param anonymousPullEnabled bool = false
+param dataEndpointEnabled bool = false
+param encryption object = {
+  status: 'disabled'
+}
+param networkRuleBypassOptions string = 'AzureServices'
+param publicNetworkAccess string = 'Enabled'
+param sku object = {
+  name: 'Basic'
+}
+param zoneRedundancy string = 'Disabled'
+
+@description('The log analytics workspace id used for logging & monitoring')
+param workspaceId string = ''
+
+// 2022-02-01-preview needed for anonymousPullEnabled
+resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = {
+  name: name
+  location: location
+  tags: tags
+  sku: sku
+  properties: {
+    adminUserEnabled: adminUserEnabled
+    anonymousPullEnabled: anonymousPullEnabled
+    dataEndpointEnabled: dataEndpointEnabled
+    encryption: encryption
+    networkRuleBypassOptions: networkRuleBypassOptions
+    publicNetworkAccess: publicNetworkAccess
+    zoneRedundancy: zoneRedundancy
+  }
+}
+
+// TODO: Update diagnostics to be its own module
+// Blocking issue: https://github.com/Azure/bicep/issues/622
+// Unable to pass in a `resource` scope or unable to use string interpolation in resource types
+resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) {
+  name: 'registry-diagnostics'
+  scope: containerRegistry
+  properties: {
+    workspaceId: workspaceId
+    logs: [
+      {
+        category: 'ContainerRegistryRepositoryEvents'
+        enabled: true
+      }
+      {
+        category: 'ContainerRegistryLoginEvents'
+        enabled: true
+      }
+    ]
+    metrics: [
+      {
+        category: 'AllMetrics'
+        enabled: true
+        timeGrain: 'PT1M'
+      }
+    ]
+  }
+}
+
+output loginServer string = containerRegistry.properties.loginServer
+output name string = containerRegistry.name
diff --git a/infra/core/monitor/loganalytics.bicep b/infra/core/monitor/loganalytics.bicep
new file mode 100644
index 0000000000..770544ccab
--- /dev/null
+++ b/infra/core/monitor/loganalytics.bicep
@@ -0,0 +1,21 @@
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
+  name: name
+  location: location
+  tags: tags
+  properties: any({
+    retentionInDays: 30
+    features: {
+      searchVersion: 1
+    }
+    sku: {
+      name: 'PerGB2018'
+    }
+  })
+}
+
+output id string = logAnalytics.id
+output name string = logAnalytics.name
diff --git a/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep
index 0143363f46..f1ffb2530a 100644
--- a/infra/core/monitor/monitoring.bicep
+++ b/infra/core/monitor/monitoring.bicep
@@ -1,7 +1,18 @@
-param applicationInsightsName string
 param location string = resourceGroup().location
 param tags object = {}
 
+param applicationInsightsName string
+param logAnalyticsName string
+
+module logAnalytics 'loganalytics.bicep' = {
+  name: 'loganalytics'
+  params: {
+    name: logAnalyticsName
+    location: location
+    tags: tags
+  }
+}
+
 module applicationInsights 'applicationinsights.bicep' = {
   name: 'applicationinsights'
   params: {
@@ -14,4 +25,5 @@ module applicationInsights 'applicationinsights.bicep' = {
 output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString
 output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey
 output applicationInsightsName string = applicationInsights.outputs.name
-
+output logAnalyticsWorkspaceId string = logAnalytics.outputs.id
+output logAnalyticsWorkspaceName string = logAnalytics.outputs.name
diff --git a/infra/core/security/registry-access.bicep b/infra/core/security/registry-access.bicep
new file mode 100644
index 0000000000..e17e404580
--- /dev/null
+++ b/infra/core/security/registry-access.bicep
@@ -0,0 +1,18 @@
+param containerRegistryName string
+param principalId string
+
+var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
+
+resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+  scope: containerRegistry // Use when specifying a scope that is different than the deployment scope
+  name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole)
+  properties: {
+    roleDefinitionId: acrPullRole
+    principalType: 'ServicePrincipal'
+    principalId: principalId
+  }
+}
+
+resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = {
+  name: containerRegistryName
+}
diff --git a/infra/main.bicep b/infra/main.bicep
index 57f2b152f4..af5681d6be 100644
--- a/infra/main.bicep
+++ b/infra/main.bicep
@@ -10,8 +10,8 @@ param environmentName string
 param location string
 
 param appServicePlanName string = ''
-param backendServiceName string = ''
 param resourceGroupName string = ''
+param logAnalyticsName string = ''
 
 param applicationInsightsName string = ''
 
@@ -60,6 +60,11 @@ param principalId string = ''
 @description('Use Application Insights for monitoring and performance tracing')
 param useApplicationInsights bool = false
 
+param containerAppsEnvironmentName string = ''
+param containerRegistryName string = ''
+param webContainerAppName string = ''
+param webAppExists bool = false
+
 var abbrs = loadJsonContent('abbreviations.json')
 var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
 var tags = { 'azd-env-name': environmentName }
@@ -94,6 +99,7 @@ module monitoring './core/monitor/monitoring.bicep' = if (useApplicationInsights
   params: {
     location: location
     tags: tags
+    logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}'
     applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}'
   }
 }
@@ -114,31 +120,40 @@ module appServicePlan 'core/host/appserviceplan.bicep' = {
   }
 }
 
-// The application frontend
-module backend 'core/host/appservice.bicep' = {
+module containerApps 'core/host/container-apps.bicep' = {
+  name: 'container-apps'
+  scope: resourceGroup
+  params: {
+    name: 'app'
+    location: location
+    containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}'
+    containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}'
+    logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName
+    applicationInsightsName: monitoring.outputs.applicationInsightsName
+  }
+}
+
+// Web frontend
+module web 'web.bicep' = {
   name: 'web'
   scope: resourceGroup
   params: {
-    name: !empty(backendServiceName) ? backendServiceName : '${abbrs.webSitesAppService}backend-${resourceToken}'
+    name: !empty(webContainerAppName) ? webContainerAppName : '${abbrs.appContainerApps}web-${resourceToken}'
     location: location
-    tags: union(tags, { 'azd-service-name': 'backend' })
-    appServicePlanId: appServicePlan.outputs.id
-    runtimeName: 'python'
-    runtimeVersion: '3.10'
-    appCommandLine: 'python3 -m gunicorn main:app'
-    scmDoBuildDuringDeployment: true
-    managedIdentity: true
-    appSettings: {
-      AZURE_STORAGE_ACCOUNT: storage.outputs.name
-      AZURE_STORAGE_CONTAINER: storageContainerName
-      AZURE_OPENAI_SERVICE: openAi.outputs.name
-      AZURE_SEARCH_INDEX: searchIndexName
-      AZURE_SEARCH_SERVICE: searchService.outputs.name
-      AZURE_OPENAI_CHATGPT_DEPLOYMENT: chatGptDeploymentName
-      AZURE_OPENAI_CHATGPT_MODEL: chatGptModelName
-      AZURE_OPENAI_EMB_DEPLOYMENT: embeddingDeploymentName
-      APPLICATIONINSIGHTS_CONNECTION_STRING: useApplicationInsights ? monitoring.outputs.applicationInsightsConnectionString : ''
-    }
+    tags: tags
+    identityName: '${abbrs.managedIdentityUserAssignedIdentities}web-${resourceToken}'
+    containerAppsEnvironmentName: containerApps.outputs.environmentName
+    containerRegistryName: containerApps.outputs.registryName
+    applicationInsightsConnectionString: useApplicationInsights ? monitoring.outputs.applicationInsightsConnectionString : ''
+    storageContainerName: storageContainerName
+    storageName: storage.outputs.name
+    searchIndexName: searchIndexName
+    searchServiceName: searchService.outputs.name
+    chatGptDeploymentName: chatGptDeploymentName
+    chatGptModelName: chatGptModelName
+    embeddingDeploymentName: embeddingDeploymentName
+    openAiServiceName: openAi.outputs.name
+    exists: webAppExists
   }
 }
 
@@ -311,7 +326,7 @@ module openAiRoleBackend 'core/security/role.bicep' = {
   scope: openAiResourceGroup
   name: 'openai-role-backend'
   params: {
-    principalId: backend.outputs.identityPrincipalId
+    principalId: web.outputs.SERVICE_WEB_IDENTITY_PRINCIPAL_ID
     roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
     principalType: 'ServicePrincipal'
   }
@@ -321,7 +336,7 @@ module storageRoleBackend 'core/security/role.bicep' = {
   scope: storageResourceGroup
   name: 'storage-role-backend'
   params: {
-    principalId: backend.outputs.identityPrincipalId
+    principalId: web.outputs.SERVICE_WEB_IDENTITY_PRINCIPAL_ID
     roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'
     principalType: 'ServicePrincipal'
   }
@@ -331,7 +346,7 @@ module searchRoleBackend 'core/security/role.bicep' = {
   scope: searchServiceResourceGroup
   name: 'search-role-backend'
   params: {
-    principalId: backend.outputs.identityPrincipalId
+    principalId: web.outputs.SERVICE_WEB_IDENTITY_PRINCIPAL_ID
     roleDefinitionId: '1407120a-92aa-4202-b7e9-c0e197c71c8f'
     principalType: 'ServicePrincipal'
   }
@@ -358,4 +373,11 @@ output AZURE_STORAGE_ACCOUNT string = storage.outputs.name
 output AZURE_STORAGE_CONTAINER string = storageContainerName
 output AZURE_STORAGE_RESOURCE_GROUP string = storageResourceGroup.name
 
-output BACKEND_URI string = backend.outputs.uri
+output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName
+output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerApps.outputs.registryLoginServer
+output AZURE_CONTAINER_REGISTRY_NAME string = containerApps.outputs.registryName
+
+output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = web.outputs.SERVICE_WEB_IDENTITY_PRINCIPAL_ID
+output SERVICE_WEB_NAME string = web.outputs.SERVICE_WEB_NAME
+output SERVICE_WEB_URI string = web.outputs.SERVICE_WEB_URI
+output SERVICE_WEB_IMAGE_NAME string = web.outputs.SERVICE_WEB_IMAGE_NAME
diff --git a/infra/main.parameters.json b/infra/main.parameters.json
index df3f727f13..3622c84960 100644
--- a/infra/main.parameters.json
+++ b/infra/main.parameters.json
@@ -54,7 +54,7 @@
       "value": "${AZURE_OPENAI_CHATGPT_DEPLOYMENT=chat}"
     },
     "useApplicationInsights": {
-      "value": "${AZURE_USE_APPLICATION_INSIGHTS=false}"
+      "value": "${AZURE_USE_APPLICATION_INSIGHTS=true}"
     }
   }
 }
diff --git a/infra/web.bicep b/infra/web.bicep
new file mode 100644
index 0000000000..926a390689
--- /dev/null
+++ b/infra/web.bicep
@@ -0,0 +1,91 @@
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+param containerAppsEnvironmentName string
+param containerRegistryName string
+param exists bool
+param identityName string
+param serviceName string = 'web'
+
+param applicationInsightsConnectionString string
+param storageContainerName string
+param storageName string
+param searchIndexName string
+param searchServiceName string
+param chatGptDeploymentName string
+param chatGptModelName string
+param embeddingDeploymentName string
+param openAiServiceName string
+
+
+resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+  name: identityName
+  location: location
+}
+
+module app 'core/host/container-app-upsert.bicep' = {
+  name: '${serviceName}-container-app-module'
+  params: {
+    name: name
+    location: location
+    tags: union(tags, { 'azd-service-name': serviceName })
+    identityName: webIdentity.name
+    exists: exists
+    containerAppsEnvironmentName: containerAppsEnvironmentName
+    containerRegistryName: containerRegistryName
+    env: [
+      {
+        name: 'AZURE_IDENTITY_ID'
+        value: webIdentity.properties.clientId
+      }
+      {
+        name: 'RUNNING_IN_PRODUCTION'
+        value: 'true'
+      }
+      {
+        name: 'AZURE_STORAGE_ACCOUNT'
+        value: storageName
+      }
+      {
+        name: 'AZURE_STORAGE_CONTAINER'
+        value: storageContainerName
+      }
+      {
+        name: 'AZURE_OPENAI_SERVICE'
+        value: openAiServiceName
+      }
+      {
+        name: 'AZURE_SEARCH_INDEX'
+        value: searchIndexName
+      }
+      {
+        name: 'AZURE_SEARCH_SERVICE'
+        value: searchServiceName
+      }
+      {
+        name: 'AZURE_OPENAI_CHATGPT_DEPLOYMENT'
+        value: chatGptDeploymentName
+      }
+      {
+        name: 'AZURE_OPENAI_CHATGPT_MODEL'
+        value: chatGptModelName
+      }
+      {
+        name: 'AZURE_OPENAI_EMB_DEPLOYMENT'
+        value: embeddingDeploymentName
+      }
+      {
+        name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
+        value: applicationInsightsConnectionString
+      }
+      ]
+    targetPort: 8000
+  }
+}
+
+
+output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId
+output SERVICE_WEB_NAME string = app.outputs.name
+output SERVICE_WEB_URI string = app.outputs.uri
+output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName
diff --git a/scripts/.dockerignore b/scripts/.dockerignore
new file mode 100644
index 0000000000..9e268ce250
--- /dev/null
+++ b/scripts/.dockerignore
@@ -0,0 +1,28 @@
+# Ignore files and directories generated by the OS
+.DS_Store
+Thumbs.db
+
+# Ignore Python bytecode files
+__pycache__/
+*.pyc
+
+# Ignore virtual environment directories
+.venv/**/*
+.venv/
+venv/
+env/
+
+# Ignore data files
+data/
+*.csv
+*.json
+*.xlsx
+*.xls
+
+# Ignore log files
+*.log
+
+# Ignore Docker-related files
+.dockerignore
+Dockerfile*
+docker-compose*
\ No newline at end of file
diff --git a/scripts/Dockerfile b/scripts/Dockerfile
new file mode 100644
index 0000000000..5748aee871
--- /dev/null
+++ b/scripts/Dockerfile
@@ -0,0 +1,14 @@
+FROM mcr.microsoft.com/devcontainers/python:3.11-bullseye
+
+RUN python -m pip install --upgrade pip
+
+WORKDIR /demo-code
+
+COPY requirements.txt .
+RUN python -m pip install -r requirements.txt
+
+COPY entrypoint.sh .
+RUN chmod +x entrypoint.sh
+
+COPY . .
+CMD bash -c ". entrypoint.sh"
diff --git a/scripts/deploy-job.sh b/scripts/deploy-job.sh
new file mode 100644
index 0000000000..64354651ec
--- /dev/null
+++ b/scripts/deploy-job.sh
@@ -0,0 +1,99 @@
+# set all the environment variables
+set +a
+source <(azd env get-values)
+set -a
+
+# build the job image
+az acr build -t prepdocs:1.0 -r $AZURE_CONTAINER_REGISTRY_NAME .
+
+# create the job
+az containerapp job create \
+    --name "job-prepdocs" --resource-group "$AZURE_RESOURCE_GROUP"  --environment $AZURE_CONTAINER_ENVIRONMENT_NAME \
+    --trigger-type "Manual" \
+    --replica-timeout 1800 --replica-retry-limit 0 --replica-completion-count 1 --parallelism 1 \
+    --image $AZURE_CONTAINER_REGISTRY_NAME.azurecr.io/prepdocs:1.0 \
+    --cpu "2" --memory "4Gi" \
+    --env-vars "AZURE_ENV_NAME=$AZURE_ENV_NAME" \
+        "AZURE_FORMRECOGNIZER_RESOURCE_GROUP=$AZURE_FORMRECOGNIZER_RESOURCE_GROUP" \
+        "AZURE_FORMRECOGNIZER_SERVICE=$AZURE_FORMRECOGNIZER_SERVICE" \
+        "AZURE_LOCATION=$AZURE_LOCATION" \
+        "AZURE_OPENAI_CHATGPT_DEPLOYMENT=$AZURE_OPENAI_CHATGPT_DEPLOYMENT" \
+        "AZURE_OPENAI_CHATGPT_MODEL=$AZURE_OPENAI_CHATGPT_MODEL" \
+        "AZURE_OPENAI_EMB_DEPLOYMENT=$AZURE_OPENAI_EMB_DEPLOYMENT" \
+        "AZURE_OPENAI_RESOURCE_GROUP=$AZURE_OPENAI_RESOURCE_GROUP" \
+        "AZURE_OPENAI_SERVICE=$AZURE_OPENAI_SERVICE" \
+        "AZURE_RESOURCE_GROUP=$AZURE_RESOURCE_GROUP" \
+        "AZURE_SEARCH_INDEX=$AZURE_SEARCH_INDEX" \
+        "AZURE_SEARCH_SERVICE=$AZURE_SEARCH_SERVICE" \
+        "AZURE_SEARCH_SERVICE_RESOURCE_GROUP=$AZURE_SEARCH_SERVICE_RESOURCE_GROUP" \
+        "AZURE_STORAGE_ACCOUNT=$AZURE_STORAGE_ACCOUNT" \
+        "AZURE_STORAGE_CONTAINER=$AZURE_STORAGE_CONTAINER" \
+        "AZURE_STORAGE_RESOURCE_GROUP=$AZURE_STORAGE_RESOURCE_GROUP" \
+        "AZURE_SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID" \
+        "AZURE_TENANT_ID=$AZURE_TENANT_ID" \
+        "AZURE_USE_APPLICATION_INSIGHTS=$AZURE_USE_APPLICATION_INSIGHTS" \
+        "BACKEND_URI=$BACKEND_URI" \
+        "LOG_FILE_NAME=$(date +%Y-%m-%d_%H-%M-%S).log" \
+        "BLOB_URL=https://jobsdemo.blob.core.windows.net/public/azure-container-apps.pdf" \
+    --registry-server $AZURE_CONTAINER_REGISTRY_NAME.azurecr.io \
+    --registry-identity system \
+    --mi-system-assigned
+
+# give the job permissions to resources
+az role assignment create --role "Storage Blob Data Contributor" --assignee `az containerapp job show -n job-prepdocs -g $AZURE_RESOURCE_GROUP -o tsv --query identity.principalId` --resource-group $AZURE_RESOURCE_GROUP
+az role assignment create --role "Cognitive Services OpenAI User" --assignee `az containerapp job show -n job-prepdocs -g $AZURE_RESOURCE_GROUP -o tsv --query identity.principalId` --resource-group $AZURE_RESOURCE_GROUP
+az role assignment create --role "Cognitive Services User" --assignee `az containerapp job show -n job-prepdocs -g $AZURE_RESOURCE_GROUP -o tsv --query identity.principalId` --resource-group $AZURE_RESOURCE_GROUP
+az role assignment create --role "Search Index Data Contributor" --assignee `az containerapp job show -n job-prepdocs -g $AZURE_RESOURCE_GROUP -o tsv --query identity.principalId` --resource-group $AZURE_RESOURCE_GROUP
+az role assignment create --role "Search Service Contributor" --assignee `az containerapp job show -n job-prepdocs -g $AZURE_RESOURCE_GROUP -o tsv --query identity.principalId` --resource-group $AZURE_RESOURCE_GROUP
+
+# az containerapp job start \
+#     --name "job-prepdocs" --resource-group "$AZURE_RESOURCE_GROUP"
+
+az storage cors add --methods GET PUT OPTIONS \
+    --origins "*" --allowed-headers "*" --exposed-headers "*" --max-age 200 \
+    --services b --account-name $AZURE_STORAGE_ACCOUNT
+
+az storage container create --name uploads --account-name $AZURE_STORAGE_ACCOUNT
+
+# build and deploy the uploader app
+
+cd ../app/docs_uploader
+
+az acr build -t doc-uploader:1.0 -r $AZURE_CONTAINER_REGISTRY_NAME .
+
+az containerapp create -n doc-uploader -g $AZURE_RESOURCE_GROUP --environment $AZURE_CONTAINER_ENVIRONMENT_NAME \
+    --image $AZURE_CONTAINER_REGISTRY_NAME.azurecr.io/doc-uploader:1.0 \
+    --cpu "1" --memory "2Gi" \
+    --env-vars "AZURE_ENV_NAME=$AZURE_ENV_NAME" \
+        "AZURE_FORMRECOGNIZER_RESOURCE_GROUP=$AZURE_FORMRECOGNIZER_RESOURCE_GROUP" \
+        "AZURE_FORMRECOGNIZER_SERVICE=$AZURE_FORMRECOGNIZER_SERVICE" \
+        "AZURE_LOCATION=$AZURE_LOCATION" \
+        "AZURE_OPENAI_CHATGPT_DEPLOYMENT=$AZURE_OPENAI_CHATGPT_DEPLOYMENT" \
+        "AZURE_OPENAI_CHATGPT_MODEL=$AZURE_OPENAI_CHATGPT_MODEL" \
+        "AZURE_OPENAI_EMB_DEPLOYMENT=$AZURE_OPENAI_EMB_DEPLOYMENT" \
+        "AZURE_OPENAI_RESOURCE_GROUP=$AZURE_OPENAI_RESOURCE_GROUP" \
+        "AZURE_OPENAI_SERVICE=$AZURE_OPENAI_SERVICE" \
+        "AZURE_RESOURCE_GROUP=$AZURE_RESOURCE_GROUP" \
+        "AZURE_SEARCH_INDEX=$AZURE_SEARCH_INDEX" \
+        "AZURE_SEARCH_SERVICE=$AZURE_SEARCH_SERVICE" \
+        "AZURE_SEARCH_SERVICE_RESOURCE_GROUP=$AZURE_SEARCH_SERVICE_RESOURCE_GROUP" \
+        "AZURE_STORAGE_ACCOUNT=$AZURE_STORAGE_ACCOUNT" \
+        "AZURE_STORAGE_CONTAINER=$AZURE_STORAGE_CONTAINER" \
+        "AZURE_STORAGE_RESOURCE_GROUP=$AZURE_STORAGE_RESOURCE_GROUP" \
+        "AZURE_SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID" \
+        "AZURE_TENANT_ID=$AZURE_TENANT_ID" \
+        "AZURE_USE_APPLICATION_INSIGHTS=$AZURE_USE_APPLICATION_INSIGHTS" \
+        "BACKEND_URI=$BACKEND_URI" \
+    --registry-server $AZURE_CONTAINER_REGISTRY_NAME.azurecr.io \
+    --registry-identity system \
+    --system-assigned \
+    --min-replicas 1 --max-replicas 1 \
+    --ingress external \
+    --target-port 8000
+
+az role assignment create --role "Contributor" --assignee `az containerapp show -n doc-uploader -g $AZURE_RESOURCE_GROUP -o tsv --query identity.principalId` --resource-group $AZURE_RESOURCE_GROUP
+az role assignment create --role "Storage Blob Data Contributor" --assignee `az containerapp show -n doc-uploader -g $AZURE_RESOURCE_GROUP -o tsv --query identity.principalId` --resource-group $AZURE_RESOURCE_GROUP
+
+# az containerapp up -n doc-uploader -g $AZURE_RESOURCE_GROUP \
+#     --registry-server $AZURE_CONTAINER_REGISTRY_NAME.azurecr.io \
+#     --source .
\ No newline at end of file
diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh
new file mode 100755
index 0000000000..b13d1a4400
--- /dev/null
+++ b/scripts/entrypoint.sh
@@ -0,0 +1,7 @@
+python3 prepdocs.py --novectors --localpdfparser --verbose \
+    --bloburl "$BLOB_URL" \
+    --logfilename "$LOG_FILE_NAME" \
+    --storageaccount "$AZURE_STORAGE_ACCOUNT" --container "$AZURE_STORAGE_CONTAINER" \
+    --searchservice "$AZURE_SEARCH_SERVICE" --index "$AZURE_SEARCH_INDEX" \
+    --openaiservice "$AZURE_OPENAI_SERVICE" --openaideployment "$AZURE_OPENAI_EMB_DEPLOYMENT" \
+    --formrecognizerservice "$AZURE_FORMRECOGNIZER_SERVICE"
diff --git a/scripts/prepdocs.py b/scripts/prepdocs.py
index b41c91bf60..5cbea30f08 100644
--- a/scripts/prepdocs.py
+++ b/scripts/prepdocs.py
@@ -1,16 +1,19 @@
 import argparse
 import base64
+import datetime
 import glob
 import html
 import io
 import os
 import re
 import time
+import urllib.request
+from urllib.parse import urlparse
 
 import openai
 from azure.ai.formrecognizer import DocumentAnalysisClient
 from azure.core.credentials import AzureKeyCredential
-from azure.identity import AzureDeveloperCliCredential
+from azure.identity import DefaultAzureCredential
 from azure.search.documents import SearchClient
 from azure.search.documents.indexes import SearchIndexClient
 from azure.search.documents.indexes.models import (
@@ -58,7 +61,7 @@ def upload_blobs(filename):
         pages = reader.pages
         for i in range(len(pages)):
             blob_name = blob_name_from_file_page(filename, i)
-            if args.verbose: print(f"\tUploading blob for page {i} -> {blob_name}")
+            if args.verbose: write_log(f"\tUploading blob for page {i} -> {blob_name}")
             f = io.BytesIO()
             writer = PdfWriter()
             writer.add_page(pages[i])
@@ -71,7 +74,7 @@ def upload_blobs(filename):
             blob_container.upload_blob(blob_name, data, overwrite=True)
 
 def remove_blobs(filename):
-    if args.verbose: print(f"Removing blobs for '{filename or ''}'")
+    if args.verbose: write_log(f"Removing blobs for '{filename or ''}'")
     blob_service = BlobServiceClient(account_url=f"https://{args.storageaccount}.blob.core.windows.net", credential=storage_creds)
     blob_container = blob_service.get_container_client(args.container)
     if blob_container.exists():
@@ -81,7 +84,7 @@ def remove_blobs(filename):
             prefix = os.path.splitext(os.path.basename(filename))[0]
             blobs = filter(lambda b: re.match(f"{prefix}-\d+\.pdf", b), blob_container.list_blob_names(name_starts_with=os.path.splitext(os.path.basename(prefix))[0]))
         for b in blobs:
-            if args.verbose: print(f"\tRemoving blob {b}")
+            if args.verbose: write_log(f"\tRemoving blob {b}")
             blob_container.delete_blob(b)
 
 def table_to_html(table):
@@ -110,7 +113,7 @@ def get_document_text(filename):
             page_map.append((page_num, offset, page_text))
             offset += len(page_text)
     else:
-        if args.verbose: print(f"Extracting text from '{filename}' using Azure Form Recognizer")
+        if args.verbose: write_log(f"Extracting text from '{filename}' using Azure Form Recognizer")
         form_recognizer_client = DocumentAnalysisClient(endpoint=f"https://{args.formrecognizerservice}.cognitiveservices.azure.com/", credential=formrecognizer_creds, headers={"x-ms-useragent": "azure-search-chat-demo/1.0.0"})
         with open(filename, "rb") as f:
             poller = form_recognizer_client.begin_analyze_document("prebuilt-layout", document = f)
@@ -150,7 +153,7 @@ def get_document_text(filename):
 def split_text(page_map, filename):
     SENTENCE_ENDINGS = [".", "!", "?"]
     WORDS_BREAKS = [",", ";", ":", " ", "(", ")", "[", "]", "{", "}", "\t", "\n"]
-    if args.verbose: print(f"Splitting '{filename}' into sections")
+    if args.verbose: write_log(f"Splitting '{filename}' into sections")
 
     def find_page(offset):
         num_pages = len(page_map)
@@ -199,7 +202,7 @@ def find_page(offset):
             # If the section ends with an unclosed table, we need to start the next section with the table.
             # If table starts inside SENTENCE_SEARCH_LIMIT, we ignore it, as that will cause an infinite loop for tables longer than MAX_SECTION_LENGTH
             # If last table starts inside SECTION_OVERLAP, keep overlapping
-            if args.verbose: print(f"Section ends with unclosed table, starting next section with the table at page {find_page(start)} offset {start} table start {last_table_start}")
+            if args.verbose: write_log(f"Section ends with unclosed table, starting next section with the table at page {find_page(start)} offset {start} table start {last_table_start}")
             start = min(end - SECTION_OVERLAP, start + last_table_start)
         else:
             start = end - SECTION_OVERLAP
@@ -227,7 +230,7 @@ def create_sections(filename, page_map, use_vectors):
         yield section
 
 def before_retry_sleep(retry_state):
-    if args.verbose: print("Rate limited on the OpenAI embeddings API, sleeping before retrying...")
+    if args.verbose: write_log("Rate limited on the OpenAI embeddings API, sleeping before retrying...")
 
 @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(15), before_sleep=before_retry_sleep)
 def compute_embedding(text):
@@ -235,7 +238,7 @@ def compute_embedding(text):
     return openai.Embedding.create(engine=args.openaideployment, input=text)["data"][0]["embedding"]
 
 def create_search_index():
-    if args.verbose: print(f"Ensuring search index {args.index} exists")
+    if args.verbose: write_log(f"Ensuring search index {args.index} exists")
     index_client = SearchIndexClient(endpoint=f"https://{args.searchservice}.search.windows.net/",
                                      credential=search_creds)
     if args.index not in index_client.list_index_names():
@@ -266,13 +269,13 @@ def create_search_index():
                     ]
                 )
             )
-        if args.verbose: print(f"Creating {args.index} search index")
+        if args.verbose: write_log(f"Creating {args.index} search index")
         index_client.create_index(index)
     else:
-        if args.verbose: print(f"Search index {args.index} already exists")
+        if args.verbose: write_log(f"Search index {args.index} already exists")
 
 def index_sections(filename, sections):
-    if args.verbose: print(f"Indexing sections from '{filename}' into search index '{args.index}'")
+    if args.verbose: write_log(f"Indexing sections from '{filename}' into search index '{args.index}'")
     search_client = SearchClient(endpoint=f"https://{args.searchservice}.search.windows.net/",
                                     index_name=args.index,
                                     credential=search_creds)
@@ -284,16 +287,16 @@ def index_sections(filename, sections):
         if i % 1000 == 0:
             results = search_client.upload_documents(documents=batch)
             succeeded = sum([1 for r in results if r.succeeded])
-            if args.verbose: print(f"\tIndexed {len(results)} sections, {succeeded} succeeded")
+            if args.verbose: write_log(f"\tIndexed {len(results)} sections, {succeeded} succeeded")
             batch = []
 
     if len(batch) > 0:
         results = search_client.upload_documents(documents=batch)
         succeeded = sum([1 for r in results if r.succeeded])
-        if args.verbose: print(f"\tIndexed {len(results)} sections, {succeeded} succeeded")
+        if args.verbose: write_log(f"\tIndexed {len(results)} sections, {succeeded} succeeded")
 
 def remove_from_index(filename):
-    if args.verbose: print(f"Removing sections from '{filename or ''}' from search index '{args.index}'")
+    if args.verbose: write_log(f"Removing sections from '{filename or ''}' from search index '{args.index}'")
     search_client = SearchClient(endpoint=f"https://{args.searchservice}.search.windows.net/",
                                     index_name=args.index,
                                     credential=search_creds)
@@ -303,10 +306,18 @@ def remove_from_index(filename):
         if r.get_count() == 0:
             break
         r = search_client.delete_documents(documents=[{ "id": d["id"] } for d in r])
-        if args.verbose: print(f"\tRemoved {len(r)} sections from index")
+        if args.verbose: write_log(f"\tRemoved {len(r)} sections from index")
         # It can take a few seconds for search results to reflect changes, so wait a bit
         time.sleep(2)
 
+def process_file(filename, upload_each_page = True):
+    if upload_each_page:
+        upload_blobs(filename)
+    page_map = get_document_text(filename)
+    sections = create_sections(os.path.basename(filename), page_map, use_vectors)
+    index_sections(os.path.basename(filename), sections)
+
+
 # refresh open ai token every 5 minutes
 def refresh_openai_token():
     if open_ai_token_cache[CACHE_KEY_TOKEN_TYPE] == 'azure_ad' and open_ai_token_cache[CACHE_KEY_CREATED_TIME] + 300 < time.time():
@@ -320,22 +331,42 @@ def read_files(path_pattern: str, use_vectors: bool):
     and execute indexing for the individual files
     """
     for filename in glob.glob(path_pattern):
-        if args.verbose: print(f"Processing '{filename}'")
-        if args.remove:
-            remove_blobs(filename)
-            remove_from_index(filename)
-        else:
-            if os.path.isdir(filename):
-                read_files(filename + "/*", use_vectors)
-                continue
-            try:
-                if not args.skipblobs:
-                    upload_blobs(filename)
-                page_map = get_document_text(filename)
-                sections = create_sections(os.path.basename(filename), page_map, use_vectors)
-                index_sections(os.path.basename(filename), sections)
-            except Exception as e:
-                print(f"\tGot an error while reading {filename} -> {e} --> skipping file")
+        read_file(filename, use_vectors)
+
+def read_file(filename, use_vectors):
+    if args.verbose: 
+        write_log(f"Processing '{filename}'")
+    if args.remove:
+        remove_blobs(filename)
+        remove_from_index(filename)
+    else:
+        if os.path.isdir(filename):
+            read_files(filename + "/*", use_vectors)
+            return
+        try:
+            if not args.skipblobs:
+                upload_blobs(filename)
+            page_map = get_document_text(filename)
+            sections = create_sections(os.path.basename(filename), page_map, use_vectors)
+            index_sections(os.path.basename(filename), sections)
+        except Exception as e:
+            write_log(f"\tGot an error while reading {filename} -> {e} --> skipping file")
+
+log_blob_client = None
+def write_log(message):
+    global log_blob_client
+    print(message)
+    if args.logfilename:
+        if not log_blob_client:
+            log_container_name = "prepdocslogs"
+            blob_service = BlobServiceClient(account_url=f"https://{args.storageaccount}.blob.core.windows.net", credential=storage_creds)
+            blob_container = blob_service.get_container_client(log_container_name)
+            if not blob_container.exists():
+                blob_container.create_container()
+            log_blob_client = blob_service.get_blob_client(container=log_container_name, blob=args.logfilename)
+            if not log_blob_client.exists():
+                log_blob_client.create_append_blob()
+        log_blob_client.append_block(message + "\n")
 
 if __name__ == "__main__":
 
@@ -343,7 +374,7 @@ def read_files(path_pattern: str, use_vectors: bool):
         description="Prepare documents by extracting content from PDFs, splitting content into sections, uploading to blob storage, and indexing in a search index.",
         epilog="Example: prepdocs.py '..\data\*' --storageaccount myaccount --container mycontainer --searchservice mysearch --index myindex -v"
         )
-    parser.add_argument("files", help="Files to be processed")
+    parser.add_argument("--files", required=False, help="Local files to be processed")
     parser.add_argument("--category", help="Value for the category field in the search index for all sections indexed in this run")
     parser.add_argument("--skipblobs", action="store_true", help="Skip uploading individual pages to Azure Blob Storage")
     parser.add_argument("--storageaccount", help="Azure Blob Storage account name")
@@ -363,10 +394,12 @@ def read_files(path_pattern: str, use_vectors: bool):
     parser.add_argument("--formrecognizerservice", required=False, help="Optional. Name of the Azure Form Recognizer service which will be used to extract text, tables and layout from the documents (must exist already)")
     parser.add_argument("--formrecognizerkey", required=False, help="Optional. Use this Azure Form Recognizer account key instead of the current user identity to login (use az login to set current user for Azure)")
     parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
+    parser.add_argument("--bloburl", required=False, help="Optional. Process this blob URL instead of a local file")
+    parser.add_argument("--logfilename", required=False, help="Optional. Save logs to a file of this name")
     args = parser.parse_args()
 
     # Use the current user identity to connect to Azure services unless a key is explicitly set for any of them
-    azd_credential = AzureDeveloperCliCredential() if args.tenantid is None else AzureDeveloperCliCredential(tenant_id=args.tenantid, process_timeout=60)
+    azd_credential = DefaultAzureCredential() if args.tenantid is None else DefaultAzureCredential(tenant_id=args.tenantid, process_timeout=60)
     default_creds = azd_credential if args.searchkey is None or args.storagekey is None else None
     search_creds = default_creds if args.searchkey is None else AzureKeyCredential(args.searchkey)
     use_vectors = not args.novectors
@@ -376,7 +409,7 @@ def read_files(path_pattern: str, use_vectors: bool):
     if not args.localpdfparser:
         # check if Azure Form Recognizer credentials are provided
         if args.formrecognizerservice is None:
-            print("Error: Azure Form Recognizer service is not provided. Please provide formrecognizerservice or use --localpdfparser for local pypdf parser.")
+            write_log("Error: Azure Form Recognizer service is not provided. Please provide formrecognizerservice or use --localpdfparser for local pypdf parser.")
             exit(1)
         formrecognizer_creds = default_creds if args.formrecognizerkey is None else AzureKeyCredential(args.formrecognizerkey)
 
@@ -401,5 +434,18 @@ def read_files(path_pattern: str, use_vectors: bool):
         if not args.remove:
             create_search_index()
 
-        print("Processing files...")
-        read_files(args.files, use_vectors)
+        if args.bloburl:
+            write_log("Downloading blob...")
+            parsed_url = urlparse(args.bloburl)
+            filename = parsed_url.path.split("/")[-1]
+            temp_folder = os.path.join(os.getcwd(), "temp")
+            if not os.path.exists(temp_folder):
+                os.makedirs(temp_folder)
+            file_path = os.path.join(temp_folder, filename)
+
+            urllib.request.urlretrieve(args.bloburl, file_path)
+            write_log("Processing file...")
+            read_file(file_path, use_vectors)
+        elif args.files:
+            write_log("Processing files...")
+            read_files(args.files, use_vectors)
diff --git a/scripts/prepdocs.sh b/scripts/prepdocs.sh
index 123b7d8b0e..97fb0bf540 100755
--- a/scripts/prepdocs.sh
+++ b/scripts/prepdocs.sh
@@ -18,4 +18,5 @@ echo 'Installing dependencies from "requirements.txt" into virtual environment'
 ./scripts/.venv/bin/python -m pip install -r scripts/requirements.txt
 
 echo 'Running "prepdocs.py"'
-./scripts/.venv/bin/python ./scripts/prepdocs.py './data/*' --storageaccount "$AZURE_STORAGE_ACCOUNT" --container "$AZURE_STORAGE_CONTAINER" --searchservice "$AZURE_SEARCH_SERVICE" --openaiservice "$AZURE_OPENAI_SERVICE" --openaideployment "$AZURE_OPENAI_EMB_DEPLOYMENT" --index "$AZURE_SEARCH_INDEX" --formrecognizerservice "$AZURE_FORMRECOGNIZER_SERVICE" --tenantid "$AZURE_TENANT_ID" -v
+# ./scripts/.venv/bin/python ./scripts/prepdocs.py --bloburl 'https://jobsdemo.blob.core.windows.net/public/azure-container-apps.pdf' --storageaccount "$AZURE_STORAGE_ACCOUNT" --container "$AZURE_STORAGE_CONTAINER" --searchservice "$AZURE_SEARCH_SERVICE" --openaiservice "$AZURE_OPENAI_SERVICE" --openaideployment "$AZURE_OPENAI_EMB_DEPLOYMENT" --index "$AZURE_SEARCH_INDEX" --formrecognizerservice "$AZURE_FORMRECOGNIZER_SERVICE" -v --novectors --localpdfparser --remove
+./scripts/.venv/bin/python ./scripts/prepdocs.py --files './data/*' --storageaccount "$AZURE_STORAGE_ACCOUNT" --container "$AZURE_STORAGE_CONTAINER" --searchservice "$AZURE_SEARCH_SERVICE" --openaiservice "$AZURE_OPENAI_SERVICE" --openaideployment "$AZURE_OPENAI_EMB_DEPLOYMENT" --index "$AZURE_SEARCH_INDEX" --formrecognizerservice "$AZURE_FORMRECOGNIZER_SERVICE" -v