Compare commits

..

No commits in common. "main" and "staging" have entirely different histories.

67 changed files with 460 additions and 1634 deletions

26
.env
View file

@ -1,25 +1,13 @@
NPM_TOKEN=
NODE_ENV=
##########################
# APP VARIABLES #
##########################
APP_NAME=hestia
# TWILIO
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=
##########################
# SECRETS #
##########################
SECRETS_PASSWORD=secret_do_not_commit_or_change_this_create_.env.local_instead
SECRETS_SALT=secret_do_not_commit_or_change_this_create_.env.local_instead
SECRETS_IV_POSITION=secret_do_not_commit_or_change_this_create_.env.local_instead
##########################
# DATABASE #
##########################
# PRISMA
DATABASE_URL="postgres://hestia:test123@localhost:5432/hestia"
DIRECT_URL="postgres://hestia:test123@localhost:5432/hestia"
##########################
# AUTHENTICATION #
##########################
# CLERK
PUBLIC_CLERK_PUBLISHABLE_KEY=secret_do_not_commit_or_change_this_create_.env.local_instead
CLERK_SECRET_KEY=secret_do_not_commit_or_change_this_create_.env.local_instead

View file

@ -1,46 +1,58 @@
name: Deployment
on:
push:
branches:
- main
- master
jobs:
deploy-app:
name: Deploy App
runs-on: ubuntu-22.04
setup:
name: Setup
runs-on: ubuntu-24.04
outputs:
cache-key: ${{ steps.cache-restore.outputs.cache-primary-key }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
- name: Bun Setup
uses: oven-sh/setup-bun@v2
with:
registry: ${{ env.GITHUB_SERVER_URL }}
username: ${{ env.GITHUB_REPOSITORY_OWNER }}
password: ${{ secrets.NPM_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
bun-version-file: '.tool-versions'
- name: Restore node_modules
uses: actions/cache/restore@v4
id: cache-restore
with:
push: true
tags: git.palko.ca/${{ env.GITHUB_REPOSITORY }}:latest
deploy-storybook:
path: |
**/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
restore-keys: |
${{ runner.os }}-bun
- name: Install
if: steps.cache-restore.outputs.cache-hit != 'true'
run: bun install
- name: Cache node_modules
if: steps.cache-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
id: cache-save
with:
path: |
**/node_modules
key: ${{ steps.cache-restore.outputs.cache-primary-key }}
storybook:
name: Deploy Storybook
runs-on: ubuntu-22.04
needs: [setup]
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
- name: Bun Setup
uses: oven-sh/setup-bun@v2
with:
registry: ${{ env.GITHUB_SERVER_URL }}
username: ${{ env.GITHUB_REPOSITORY_OWNER }}
password: ${{ secrets.NPM_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
bun-version-file: '.tool-versions'
- name: Restore node_modules
uses: actions/cache/restore@v4
id: cache-restore
with:
push: true
file: Dockerfile.storybook
tags: git.palko.ca/${{ env.GITHUB_REPOSITORY }}-storybook:latest
path: |
**/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
- name: Build Storybook
run: bun build-storybook

View file

@ -1,17 +1,7 @@
name: PR Gate
on:
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read # to fetch code (actions/checkout)
branches: ['*']
jobs:
checks:
name: Checks
@ -27,11 +17,11 @@ jobs:
run: bun install
- name: Lint
run: bun lint
- name: Test
run: bun test
- name: Build
run: bun run build
- name: Svelte Check
run: bun check
- name: Prisma Check
run: bun prisma:validate
- name: Test
run: bun run test:unit

3
.gitignore vendored
View file

@ -14,9 +14,6 @@ node_modules
Thumbs.db
.idea
# Storybook
storybook-static
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

View file

@ -2,4 +2,3 @@
package-lock.json
pnpm-lock.yaml
yarn.lock
*.md

View file

@ -1,17 +0,0 @@
FROM oven/bun:1.2-alpine AS build
WORKDIR /opt/build
COPY . .
RUN bun install --frozen-lockfile \
&& bun run build
FROM node:18-alpine3.21
WORKDIR /opt/app
COPY ./package.json ./package.json
COPY --from=build --chown=1000:1000 /opt/build/node_modules/ /opt/app/node_modules/
COPY --from=build --chown=1000:1000 /opt/build/build /opt/app
ENTRYPOINT [ "node", "index.js" ]

View file

@ -1,11 +0,0 @@
FROM oven/bun:1.2-alpine AS build
WORKDIR /opt/build
COPY . .
RUN bun install --frozen-lockfile \
&& bun run build-storybook
FROM nginx:alpine3.21
COPY --from=build --chown=1000:1000 /opt/build/storybook-static /usr/share/nginx/html

View file

@ -1,17 +1,13 @@
# Hestia
<!--toc:start-->
- [Hestia](#hestia)
- [Setup](#setup)
- [Developing](#developing)
- [Modifying Database Schema](#modifying-database-schema)
- [Building](#building)
- [Stack](#stack) - [Frontend](#frontend) - [Backend](#backend) - [Tools](#tools)
<!--toc:end-->
Hestia is an early stage project
- [Setup](#setup)
- [Developing](#developing)
- [Modifying Database Schema](#modifying-database-schema)
- [Building](#building)
- [Stack](#stack)
## Setup
```bash

282
bun.lock
View file

@ -9,7 +9,7 @@
"@inlang/paraglide-sveltekit": "^0.15.0",
"@pothos/core": "^4.3.0",
"@pothos/plugin-prisma": "^4.4.0",
"@prisma/client": "6.3.1",
"@prisma/client": "6.0.1",
"@tailwindcss/typography": "^0.5.15",
"clerk-sveltekit": "https://pkg.pr.new/wobsoriano/clerk-sveltekit@ca15d4e",
"clsx": "^2.1.1",
@ -28,19 +28,19 @@
"@chromatic-com/storybook": "^3.2.2",
"@eslint/compat": "^1.2.3",
"@playwright/test": "^1.45.3",
"@storybook/addon-essentials": "^8.5.0",
"@storybook/addon-interactions": "^8.5.0",
"@storybook/addon-essentials": "^8.4.7",
"@storybook/addon-interactions": "^8.4.7",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-svelte-csf": "^5.0.0-next.13",
"@storybook/addon-themes": "^8.5.0",
"@storybook/blocks": "^8.5.0",
"@storybook/svelte": "^8.5.0",
"@storybook/sveltekit": "^8.5.0",
"@storybook/test": "^8.5.0",
"@sveltejs/adapter-node": "^5.2.12",
"@storybook/addon-themes": "^8.4.7",
"@storybook/blocks": "^8.4.7",
"@storybook/svelte": "^8.4.7",
"@storybook/sveltekit": "^8.4.7",
"@storybook/test": "^8.4.7",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.15.3",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@types/bun": "^1.1.15",
"@types/bun": "^1.1.14",
"autoprefixer": "^10.4.20",
"daisyui": "^4.12.22",
"eslint": "^9.7.0",
@ -52,8 +52,8 @@
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.6",
"prettier-plugin-tailwindcss": "^0.6.5",
"prisma": "6.3.1",
"storybook": "^8.5.0",
"prisma": "^6.0.1",
"storybook": "^8.4.7",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.9",
@ -75,17 +75,17 @@
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
"@babel/runtime": ["@babel/runtime@7.26.7", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ=="],
"@babel/runtime": ["@babel/runtime@7.26.0", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw=="],
"@chromatic-com/storybook": ["@chromatic-com/storybook@3.2.4", "", { "dependencies": { "chromatic": "^11.15.0", "filesize": "^10.0.12", "jsonfile": "^6.1.0", "react-confetti": "^6.1.0", "strip-ansi": "^7.1.0" }, "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-5/bOOYxfwZ2BktXeqcCpOVAoR6UCoeART5t9FVy22hoo8F291zOuX4y3SDgm10B1GVU/ZTtJWPT2X9wZFlxYLg=="],
"@chromatic-com/storybook": ["@chromatic-com/storybook@3.2.3", "", { "dependencies": { "chromatic": "^11.15.0", "filesize": "^10.0.12", "jsonfile": "^6.1.0", "react-confetti": "^6.1.0", "strip-ansi": "^7.1.0" }, "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-3+hfANx79kIjP1qrOSLxpoAXOiYUA0S7A0WI0A24kASrv7USFNNW8etR5TjUilMb0LmqKUn3wDwUK2h6aceQ9g=="],
"@clerk/backend": ["@clerk/backend@1.21.4", "", { "dependencies": { "@clerk/shared": "^2.20.4", "@clerk/types": "^4.40.0", "cookie": "0.7.0", "snakecase-keys": "5.4.4", "tslib": "2.4.1" } }, "sha512-PHJzBJrTxBAvHwscXwUwpippT7nHhphgycVcFb3655Dq6q0nRdKfo5GlkDHscEKxLOdmppB/168nXsVwSWf86w=="],
"@clerk/shared": ["@clerk/shared@2.20.14", "", { "dependencies": { "@clerk/types": "^4.44.0", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.7.0", "swr": "^2.2.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-cQ5cRYHHLmYYSFg6OLuNUHxM6W4r5rabKOupP2jlGLns8WMHfbxNiLf/9nPUbLKDjWKG2Ott/nRN9iZQwcyfew=="],
"@clerk/shared": ["@clerk/shared@2.20.8", "", { "dependencies": { "@clerk/types": "^4.41.0", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.7.0", "swr": "^2.2.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-I94MUUpEKi31KMiK2ZL6hsvNsgRuqqtZ+dQlE8zEUZKW+7hoeGimUQTKf7yr2zQ6XDhpGTt+63Cysx7kIny7yQ=="],
"@clerk/themes": ["@clerk/themes@2.2.12", "", { "dependencies": { "@clerk/types": "^4.44.0", "tslib": "2.4.1" } }, "sha512-2KLihkWClEUc5ZkIzNU9cTcpWtzxAm2GZRCSI0KUKZA/0V6glhmnxSSk+AQXzO9GpbyQm+UDIOQQSvQ10hnnFQ=="],
"@clerk/themes": ["@clerk/themes@2.2.7", "", { "dependencies": { "@clerk/types": "^4.41.0", "tslib": "2.4.1" } }, "sha512-LM1cOYtG7pbtYotpZnZfiL0pzug1tSYOixw90vzGLXy9354MvWOJ2Hq+7xVslbUheL/eAMouoHzRMtpMGk8rvw=="],
"@clerk/types": ["@clerk/types@4.44.0", "", { "dependencies": { "csstype": "3.1.1" } }, "sha512-L9UNS21sQ7OAgXKUWSMaZWayI/qmVM4DGvGCAlL0tpzbzdFU1nkxRaMOT3FkmWKSahIq4BI+vJHfMC1ki9uuGg=="],
"@clerk/types": ["@clerk/types@4.41.0", "", { "dependencies": { "csstype": "3.1.1" } }, "sha512-pSIDJ6WKmkPLAThzuXABFt/K581iLjsZSOU7e+hDdb1PRElKi11stMJruDU8UjLBNFksZUG5Cfkt0iGrVQmpXw=="],
"@envelop/core": ["@envelop/core@5.0.3", "", { "dependencies": { "@envelop/types": "5.0.0", "tslib": "^2.5.0" } }, "sha512-SE3JxL7odst8igN6x77QWyPpXKXz/Hs5o5Y27r+9Br6WHIhkW90lYYVITWIJQ/qYgn5PkpbaVgeFY9rgqQaZ/A=="],
@ -153,7 +153,7 @@
"@eslint/eslintrc": ["@eslint/eslintrc@3.2.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w=="],
"@eslint/js": ["@eslint/js@9.19.0", "", {}, "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ=="],
"@eslint/js": ["@eslint/js@9.18.0", "", {}, "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.5", "", {}, "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ=="],
@ -301,7 +301,7 @@
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@playwright/test": ["@playwright/test@1.50.0", "", { "dependencies": { "playwright": "1.50.0" }, "bin": { "playwright": "cli.js" } }, "sha512-ZGNXbt+d65EGjBORQHuYKj+XhCewlwpnSd/EDuLPZGSiEWmgOJB5RmMCCYGy5aMfTs9wx61RivfDKi8H/hcMvw=="],
"@playwright/test": ["@playwright/test@1.49.1", "", { "dependencies": { "playwright": "1.49.1" }, "bin": { "playwright": "cli.js" } }, "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g=="],
"@polka/url": ["@polka/url@1.0.0-next.28", "", {}, "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw=="],
@ -309,141 +309,133 @@
"@pothos/plugin-prisma": ["@pothos/plugin-prisma@4.4.0", "", { "dependencies": { "@prisma/generator-helper": "^6.0.0" }, "peerDependencies": { "@pothos/core": "*", "@prisma/client": "*", "graphql": ">=16.6.0", "typescript": ">=4.7.2" }, "bin": { "prisma-pothos-types": "bin/generator.js" } }, "sha512-TlXJ8QVOihpPbJGGHwOv2MUFv61xHU2xgGyTVp7KWNLQ6hzP9RH3nSQY7H1pNLOYR5wK+RyREJcaswVNwtxuqw=="],
"@prisma/client": ["@prisma/client@6.3.1", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-ARAJaPs+eBkemdky/XU3cvGRl+mIPHCN2lCXsl5Vlb0E2gV+R6IN7aCI8CisRGszEZondwIsW9Iz8EJkTdykyA=="],
"@prisma/client": ["@prisma/client@6.0.1", "", { "peerDependencies": { "prisma": "*" }, "optionalPeers": ["prisma"] }, "sha512-60w7kL6bUxz7M6Gs/V+OWMhwy94FshpngVmOY05TmGD0Lhk+Ac0ZgtjlL6Wll9TD4G03t4Sq1wZekNVy+Xdlbg=="],
"@prisma/debug": ["@prisma/debug@6.3.1", "", {}, "sha512-RrEBkd+HLZx+ydfmYT0jUj7wjLiS95wfTOSQ+8FQbvb6vHh5AeKfEPt/XUQ5+Buljj8hltEfOslEW57/wQIVeA=="],
"@prisma/debug": ["@prisma/debug@6.2.1", "", {}, "sha512-0KItvt39CmQxWkEw6oW+RQMD6RZ43SJWgEUnzxN8VC9ixMysa7MzZCZf22LCK5DSooiLNf8vM3LHZm/I/Ni7bQ=="],
"@prisma/engines": ["@prisma/engines@6.3.1", "", { "dependencies": { "@prisma/debug": "6.3.1", "@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", "@prisma/fetch-engine": "6.3.1", "@prisma/get-platform": "6.3.1" } }, "sha512-sXdqEVLyGAJ5/iUoG/Ea5AdHMN71m6PzMBWRQnLmhhOejzqAaEr8rUd623ql6OJpED4s/U4vIn4dg1qkF7vGag=="],
"@prisma/engines": ["@prisma/engines@6.2.1", "", { "dependencies": { "@prisma/debug": "6.2.1", "@prisma/engines-version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69", "@prisma/fetch-engine": "6.2.1", "@prisma/get-platform": "6.2.1" } }, "sha512-lTBNLJBCxVT9iP5I7Mn6GlwqAxTpS5qMERrhebkUhtXpGVkBNd/jHnNJBZQW4kGDCKaQg/r2vlJYkzOHnAb7ZQ=="],
"@prisma/engines-version": ["@prisma/engines-version@6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", "", {}, "sha512-R/ZcMuaWZT2UBmgX3Ko6PAV3f8//ZzsjRIG1eKqp3f2rqEqVtCv+mtzuH2rBPUC9ujJ5kCb9wwpxeyCkLcHVyA=="],
"@prisma/engines-version": ["@prisma/engines-version@6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69", "", {}, "sha512-7tw1qs/9GWSX6qbZs4He09TOTg1ff3gYsB3ubaVNN0Pp1zLm9NC5C5MZShtkz7TyQjx7blhpknB7HwEhlG+PrQ=="],
"@prisma/fetch-engine": ["@prisma/fetch-engine@6.3.1", "", { "dependencies": { "@prisma/debug": "6.3.1", "@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0", "@prisma/get-platform": "6.3.1" } }, "sha512-HOf/0umOgt+/S2xtZze+FHKoxpVg4YpVxROr6g2YG09VsI3Ipyb+rGvD6QGbCqkq5NTWAAZoOGNL+oy7t+IhaQ=="],
"@prisma/fetch-engine": ["@prisma/fetch-engine@6.2.1", "", { "dependencies": { "@prisma/debug": "6.2.1", "@prisma/engines-version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69", "@prisma/get-platform": "6.2.1" } }, "sha512-OO7O9d6Mrx2F9i+Gu1LW+DGXXyUFkP7OE5aj9iBfA/2jjDXEJjqa9X0ZmM9NZNo8Uo7ql6zKm6yjDcbAcRrw1A=="],
"@prisma/generator-helper": ["@prisma/generator-helper@6.2.1", "", { "dependencies": { "@prisma/debug": "6.2.1" } }, "sha512-7Ws8DCXGan7hhaFMERXYdmhsudvSzEsrTttJEC7ubZJidvyimS12m3xpM+dLTt+NAShJ7Op7PgF+Mal2jf6xfg=="],
"@prisma/get-platform": ["@prisma/get-platform@6.3.1", "", { "dependencies": { "@prisma/debug": "6.3.1" } }, "sha512-AYLq6Hk9xG73JdLWJ3Ip9Wg/vlP7xPvftGBalsPzKDOHr/ImhwJ09eS8xC2vNT12DlzGxhfk8BkL0ve2OriNhQ=="],
"@prisma/get-platform": ["@prisma/get-platform@6.2.1", "", { "dependencies": { "@prisma/debug": "6.2.1" } }, "sha512-zp53yvroPl5m5/gXYLz7tGCNG33bhG+JYCm74ohxOq1pPnrL47VQYFfF3RbTZ7TzGWCrR3EtoiYMywUBw7UK6Q=="],
"@repeaterjs/repeater": ["@repeaterjs/repeater@3.0.6", "", {}, "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA=="],
"@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.30.1", "", { "os": "android", "cpu": "arm" }, "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q=="],
"@rollup/plugin-json": ["@rollup/plugin-json@6.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.30.1", "", { "os": "android", "cpu": "arm64" }, "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w=="],
"@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.1", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q=="],
"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.32.0", "", { "os": "android", "cpu": "arm" }, "sha512-G2fUQQANtBPsNwiVFg4zKiPQyjVKZCUdQUol53R8E71J7AsheRMV/Yv/nB8giOcOVqP7//eB5xPqieBYZe9bGg=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.30.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-qhFwQ+ljoymC+j5lXRv8DlaJYY/+8vyvYmVx074zrLsu5ZGWYsJNLjPPVJJjhZQpyAKUGPydOq9hRLLNvh1s3A=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-44n/X3lAlWsEY6vF8CzgCx+LQaoqWGN7TzUfbJDiTIOjJm4+L2Yq+r5a8ytQRGyPqgJDs3Rgyo8eVL7n9iW6AQ=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-F9ct0+ZX5Np6+ZDztxiGCIvlCaW87HBdHcozUfsHnj1WCUTBUubAoanhHUfnUHZABlElyRikI0mgcw/qdEm2VQ=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.32.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-JpsGxLBB2EFXBsTLHfkZDsXSpSmKD3VxXCgBQtlPcuAqB8TlqtLcbeMhxXQkCDv1avgwNjF8uEIbq5p+Cee0PA=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-wegiyBT6rawdpvnD9lmbOpx5Sph+yVZKHbhnSP9MqUEDX08G4UzMU+D87jrazGE7lRSyTRs6NEYHtzfkJ3FjjQ=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-3pA7xecItbgOs1A5H58dDvOUEboG5UfpTq3WzAdF54acBbUM+olDJAPkgj1GRJ4ZqE12DZ9/hNS2QZk166v92A=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.30.1", "", { "os": "linux", "cpu": "none" }, "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Y7XUZEVISGyge51QbYyYAEHwpGgmRrAxQXO3siyYo2kmaj72USSG8LtlQQgAtlGfxYiOwu+2BdbPjzEpcOpRmQ=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.30.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-r7/OTF5MqeBrZo5omPXcTnjvv1GsrdH8a8RerARvDFiDwFpDVDnJyByYM/nX+mvks8XXsgPUxkwe/ltaX2VH7w=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.30.1", "", { "os": "linux", "cpu": "none" }, "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-HJbifC9vex9NqnlodV2BHVFNuzKL5OnsV2dvTw6e1dpZKkNjPG6WUq+nhEYV6Hv2Bv++BXkwcyoGlXnPrjAKXw=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.30.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.32.0", "", { "os": "linux", "cpu": "none" }, "sha512-VAEzZTD63YglFlWwRj3taofmkV1V3xhebDXffon7msNz4b14xKsz7utO6F8F4cqt8K/ktTl9rm88yryvDpsfOw=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.32.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Sts5DST1jXAc9YH/iik1C9QRsLcCoOScf3dfbY5i4kH9RJpKxiTBXqm7qU5O6zTXBTEZry69bGszr3SMgYmMcQ=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.32.0", "", { "os": "linux", "cpu": "none" }, "sha512-qhlXeV9AqxIyY9/R1h1hBD6eMvQCO34ZmdYvry/K+/MBs6d1nRFLm6BOiITLVI+nFAAB9kUB6sdJRKyVHXnqZw=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.32.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-8ZGN7ExnV0qjXa155Rsfi6H8M4iBBwNLBM9lcVS+4NcSzOFaNqmt7djlox8pN1lWrRPMRRQ8NeDlozIGx3Omsw=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.30.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-VDzNHtLLI5s7xd/VubyS10mq6TxvZBp+4NRWoW+Hi3tgV05RtVm4qK99+dClwTN1McA6PHwob6DEJ6PlXbY83A=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-qcb9qYDlkxz9DxJo7SDhWxTWV1gFuwznjbTiov289pASxlfGbaOD54mgbs9+z94VwrXtKTu+2RqwlSTbiOqxGg=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-pFDdotFDMXW2AXVbfdUEfidPAk/OtwE/Hd4eYMTNVVaCQ6Yl8et0meDaKNL63L44Haxv4UExpv9ydSf3aSayDg=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.32.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-/TG7WfrCAjeRNDvI4+0AAMoHxea/USWhAzf9PVDFHbcqrQ7hMMKp4jZIy4VEjk72AAfN5k4TiSMRXRKf/0akSw=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-5hqO5S3PTEO2E5VjCePxv40gIgyS2KvO7E7/vvC/NbIW4SIRamkMr1hqj+5Y67fbBWv/bQLB6KelBQmXlyCjWA=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og=="],
"@sinclair/typebox": ["@sinclair/typebox@0.31.28", "", {}, "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ=="],
"@storybook/addon-actions": ["@storybook/addon-actions@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "@types/uuid": "^9.0.1", "dequal": "^2.0.2", "polished": "^4.2.2", "uuid": "^9.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-oBBSpOJ6/rCdbdU1JxGCLernaCxALLWDIeZk6tLoQbtbsx/czD1sodqjcujjKwbQwNyZTf8xR8zsCSzG06dWDw=="],
"@storybook/addon-actions": ["@storybook/addon-actions@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0", "@types/uuid": "^9.0.1", "dequal": "^2.0.2", "polished": "^4.2.2", "uuid": "^9.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-6CW9+17rk5eNx6I8EKqCxRKtsJFTR/lHL+xiJ6/iBWApIm8sg63vhXvUTJ58UixmIkT5oLh0+ESNPh+x10D8fw=="],
"@storybook/addon-backgrounds": ["@storybook/addon-backgrounds@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-4NFRFblPbRP3D4o4sSbJ1x9SMncP4+SHdSqKIovTjb+zOhqYPFYWMTinzEndUnBSDGREldHUvHjROuxrD/0qzA=="],
"@storybook/addon-backgrounds": ["@storybook/addon-backgrounds@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-lzyFLs7niNsqlhH5kdUrp7htLiMIcjY50VLWe0PaeJ6T6GZ7X9qhQzROAUV6cGqzyd8A6y/LzIUntDPMVEm/6g=="],
"@storybook/addon-controls": ["@storybook/addon-controls@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "dequal": "^2.0.2", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-RA/SPXW1chfsWaV8Lv/aXJNZJ8hasDEXQ1C5xRCt+T8DFvPqRZGgUfIpsiZ80AKp5RzufT9KL+39piPMljhKXA=="],
"@storybook/addon-controls": ["@storybook/addon-controls@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0", "dequal": "^2.0.2", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-1fivx77A/ahObrPl0L66o9i9MUNfqXxsrpekne5gjMNXw9XJFIRNUe/ddL4CMmwu7SgVbj2QV+q5E5mlnZNTJw=="],
"@storybook/addon-docs": ["@storybook/addon-docs@8.5.1", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/blocks": "8.5.1", "@storybook/csf-plugin": "8.5.1", "@storybook/react-dom-shim": "8.5.1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-XhELkuNFOa8q2rF/AXTwnKZth7lCFqkfR5VuEAQ+g9hv2p6I/VGlTddylzjdaZKhiy4p8O9DrzGdLFj+oxOpMw=="],
"@storybook/addon-docs": ["@storybook/addon-docs@8.5.0", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/blocks": "8.5.0", "@storybook/csf-plugin": "8.5.0", "@storybook/react-dom-shim": "8.5.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-REwLSr1VgOVNJZwP3y3mldhOjBHlM5fqTvq/tC8NaYpAzx9O4rZdoUSZxW3tYtoNoYrHpB8kzRTeZl8WSdKllw=="],
"@storybook/addon-essentials": ["@storybook/addon-essentials@8.5.1", "", { "dependencies": { "@storybook/addon-actions": "8.5.1", "@storybook/addon-backgrounds": "8.5.1", "@storybook/addon-controls": "8.5.1", "@storybook/addon-docs": "8.5.1", "@storybook/addon-highlight": "8.5.1", "@storybook/addon-measure": "8.5.1", "@storybook/addon-outline": "8.5.1", "@storybook/addon-toolbars": "8.5.1", "@storybook/addon-viewport": "8.5.1", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-jPGrZ7j+RWistrsgpvjUBvLpWRuOeDNdV014ggHBxDMNX9GWb1GSubWW2Tlo7BfOuUvjICVAjI4KMp/IC/jwZg=="],
"@storybook/addon-essentials": ["@storybook/addon-essentials@8.5.0", "", { "dependencies": { "@storybook/addon-actions": "8.5.0", "@storybook/addon-backgrounds": "8.5.0", "@storybook/addon-controls": "8.5.0", "@storybook/addon-docs": "8.5.0", "@storybook/addon-highlight": "8.5.0", "@storybook/addon-measure": "8.5.0", "@storybook/addon-outline": "8.5.0", "@storybook/addon-toolbars": "8.5.0", "@storybook/addon-viewport": "8.5.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-RrHRdaw2j3ugZiYQ6OHt3Ff08ID4hwAvipqULEsbEnEw3VlXOaW/MT5e2M7kW3MHskQ3iJ6XAD1Y1rNm432Pzw=="],
"@storybook/addon-highlight": ["@storybook/addon-highlight@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-nhwx39DuWy2OFP+AQg8EzYP3giM+rQ0OIdAXgAjDVdKk2sGj43gwNYS9wQzXeczEUiSEjQk0JJwBqjF+GtSrag=="],
"@storybook/addon-highlight": ["@storybook/addon-highlight@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-/JxYzMK5aJSYs0K/0eAEFyER2dMoxqwM891MdnkNwLFdyrM58lzHee00F9oEX6zeQoRUNQPRepq0ui2PvbTMGw=="],
"@storybook/addon-interactions": ["@storybook/addon-interactions@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/instrumenter": "8.5.1", "@storybook/test": "8.5.1", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-tXCKBIWjwhVuSRRoEiPx+u0D4oqMkctTzysfoCw2sqftIT8t2yHyviX29s87z2NH+DNqzBGGDG1UUaLe5qq3Fw=="],
"@storybook/addon-interactions": ["@storybook/addon-interactions@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/instrumenter": "8.5.0", "@storybook/test": "8.5.0", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-vX1a8qS7o/W3kEzfL/CqOj/Rr6UlGLT/n0KXMpfIhx63tzxe1a1qGpFLL0h0zqAVPHZIOu9humWMKri5Iny6oA=="],
"@storybook/addon-measure": ["@storybook/addon-measure@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "tiny-invariant": "^1.3.1" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-Goc/IRh0aYT7zfDP9fgwL+DFX52DylanoBf0uGf59IQ7sEJHbwWm0OpiSEDo+NbtytbG83UOQamT7aQxhQo7Zw=="],
"@storybook/addon-measure": ["@storybook/addon-measure@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0", "tiny-invariant": "^1.3.1" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-e8pJy2sICyj0Ff0W1PFc6HPE6PqcjnnHtfuDaO3M9uSKJLYkpTWJ8i1VSP178f8seq44r5/PdQCHqs5q5l3zgw=="],
"@storybook/addon-outline": ["@storybook/addon-outline@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-LM3wG5bUgAAEgDS4MD1dw2VStduSYTMc/rNgaTExVVr7pPeuAgkfyIUriP3P0i7x5jweSb2aGzaTuy3PUHAWfg=="],
"@storybook/addon-outline": ["@storybook/addon-outline@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-r12sk1b38Ph6NroWAOTfjbJ/V+gDobm7tKQQlbSDf6fgX7cqyPHmKjfNDCOCQpXouZm/Jm+41zd758PW+Yt4ng=="],
"@storybook/addon-styling-webpack": ["@storybook/addon-styling-webpack@1.0.1", "", { "dependencies": { "@storybook/node-logger": "^8.0.0-alpha.10" }, "peerDependencies": { "webpack": "^5.0.0" } }, "sha512-5n+SXPfMTc4m7sWaJWPWjoHYWc6/B111M2Ia55toQ3GV4ON4vVlTgH9FX+EgCkDticElj99HLTMDJkHRj2yvkg=="],
"@storybook/addon-svelte-csf": ["@storybook/addon-svelte-csf@5.0.0-next.23", "", { "dependencies": { "@storybook/csf": "^0.1.11", "@storybook/docs-tools": "^8.0.0", "@storybook/types": "^8.0.0", "dedent": "^1.5.3", "es-toolkit": "^1.26.1", "esrap": "^1.2.2", "magic-string": "^0.30.12", "svelte-ast-print": "^0.4.0", "zimmerframe": "^1.1.2" }, "peerDependencies": { "@storybook/svelte": "^8.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0" } }, "sha512-GAdvA1f5wMhfrkP9znwDZzBFEopkELTRz8US+5XqucYHFfATiJIOALB+dW+CsUuknsfPxaA/Yqz9314ZYrT4iQ=="],
"@storybook/addon-themes": ["@storybook/addon-themes@8.5.1", "", { "dependencies": { "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-xgPRznXvD95iir+mK/WMJ5QmPeiWfNcCttCNeSVsqHmSlN+5OW28N91V0tZmqDstBW5/qTd1itar23Xrpf8qKA=="],
"@storybook/addon-themes": ["@storybook/addon-themes@8.5.0", "", { "dependencies": { "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-pBNut4sLfcOeLBvWdNAJ3cxv/BfMSTmJcUtSzE4G+1pVNsBbGF+T2f/GM0IjaM0K8Ft03VDzeEAB64nluDS4RA=="],
"@storybook/addon-toolbars": ["@storybook/addon-toolbars@8.5.1", "", { "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-01Odzujfq/g9u1ZTmH/X3I9cCnsNzG/wuyhzFr/T99jerx8QG/U45iYYph2Ytw6A5AtYyCnPYmsTsI+phjUvuA=="],
"@storybook/addon-toolbars": ["@storybook/addon-toolbars@8.5.0", "", { "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-q3yYYO2WX8K2DYNM++FzixGDjzYaeREincgsl2WXYXrcuGb5hkOoOgRiAQL8Nz9NQ1Eo+B/yZxrhG/5VoVhUUQ=="],
"@storybook/addon-viewport": ["@storybook/addon-viewport@8.5.1", "", { "dependencies": { "memoizerific": "^1.11.3" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-kKCXZT3keUEQulv2tOzRSl/GdFA2JeFjHmks/n7qQLY0zDqdx/C7K9jUECcrOJiLclZwTJvHA3YXrglVJoa6Hw=="],
"@storybook/addon-viewport": ["@storybook/addon-viewport@8.5.0", "", { "dependencies": { "memoizerific": "^1.11.3" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-MlhVELImk9YzjEgGR2ciLC8d5tUSGcO7my4kWIClN0VyTRcvG4ZfwrsEC+jN3/l52nrgjLmKrDX5UAGZm6w5mQ=="],
"@storybook/blocks": ["@storybook/blocks@8.5.1", "", { "dependencies": { "@storybook/csf": "0.1.12", "@storybook/icons": "^1.2.12", "ts-dedent": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^8.5.1" }, "optionalPeers": ["react", "react-dom"] }, "sha512-xUjnOa9udmHhlBTZ+bmMHeU1M9a5OnvnX8urQ0TrNpSyHH7HoPd3xZC4fzz73nSJNMVHIYMZYsz2pj/WfeA/hg=="],
"@storybook/blocks": ["@storybook/blocks@8.5.0", "", { "dependencies": { "@storybook/csf": "0.1.12", "@storybook/icons": "^1.2.12", "ts-dedent": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^8.5.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-2sTOgjH/JFOgWnpqkKjpKVvKAgUaC9ZBjH1gnCoA5dne/SDafYaCAYfv6yZn7g2Xm1sTxWCAmMIUkYSALeWr+w=="],
"@storybook/builder-vite": ["@storybook/builder-vite@8.5.1", "", { "dependencies": { "@storybook/csf-plugin": "8.5.1", "browser-assert": "^1.2.1", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.1", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-m7nzMmXL8ySRDp3AWsd18xB/mRVFdGnCbXeC2HREQVsu1WFkvcHtksvF4x1BOeeL73eokD2/GzgpCjAS0xVvbw=="],
"@storybook/builder-vite": ["@storybook/builder-vite@8.5.0", "", { "dependencies": { "@storybook/csf-plugin": "8.5.0", "browser-assert": "^1.2.1", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.5.0", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-GVJFjAxX/mL3bmXX6N619ShuYprkh6Ix08JU6QGNf/tTkG92BxjgCqQdfovBrviDhFyO2bhkdlEp6ujMo5CbZA=="],
"@storybook/components": ["@storybook/components@8.5.1", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-dgZfIIRdI7yA9bYb1rhWzbvU4AnbndAeNhLouxHJkUR5r2Ycp9mJba5UNynN1slgDOxB+VMnq1fWKyfWQrBqnw=="],
"@storybook/components": ["@storybook/components@8.5.0", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-DhaHtwfEcfWYj3ih/5RBSDHe3Idxyf+oHw2/DmaLKJX6MluhdK3ZqigjRcTmA9Gj/SbR4CkHEEtDzAvBlW0BYw=="],
"@storybook/core": ["@storybook/core@8.5.1", "", { "dependencies": { "@storybook/csf": "0.1.12", "better-opn": "^3.0.2", "browser-assert": "^1.2.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0", "esbuild-register": "^3.5.0", "jsdoc-type-pratt-parser": "^4.0.0", "process": "^0.11.10", "recast": "^0.23.5", "semver": "^7.6.2", "util": "^0.12.5", "ws": "^8.2.3" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"] }, "sha512-4zxjclENpZYuNY1fZJE4a7hd8Ho/SiOSN2B57fsIi1qCpKax3JU3J59ZcAWT0iidy5qgM2qMcWbrl0Bl/tWamA=="],
"@storybook/core": ["@storybook/core@8.5.0", "", { "dependencies": { "@storybook/csf": "0.1.12", "better-opn": "^3.0.2", "browser-assert": "^1.2.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0", "esbuild-register": "^3.5.0", "jsdoc-type-pratt-parser": "^4.0.0", "process": "^0.11.10", "recast": "^0.23.5", "semver": "^7.6.2", "util": "^0.12.5", "ws": "^8.2.3" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"] }, "sha512-apborO6ynns7SeydBSqE9o0zT6JSU+VY4gLFPJROGcconvSW4bS5xtJCsgjlulceyWVxepFHGXl4jEZw+SktXA=="],
"@storybook/csf": ["@storybook/csf@0.1.12", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-9/exVhabisyIVL0VxTCxo01Tdm8wefIXKXfltAPTSr8cbLn5JAxGQ6QV3mjdecLGEOucfoVhAKtJfVHxEK1iqw=="],
"@storybook/csf": ["@storybook/csf@0.1.13", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q=="],
"@storybook/csf-plugin": ["@storybook/csf-plugin@8.5.1", "", { "dependencies": { "unplugin": "^1.3.1" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-8GFrQgJ+/hzWAj9o4XK8m7UFPLxf0w3RwX0ZMPeb6zDhq/1BUE97AjKFb4Oexkh4I67Pycv4gRUOY9+tXF/1DA=="],
"@storybook/csf-plugin": ["@storybook/csf-plugin@8.5.0", "", { "dependencies": { "unplugin": "^1.3.1" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-cs6ogviNyLG1h9J8Sb47U3DqIrQmn2EHm4ta3fpCeV3ABbrMgbzYyxtmybz4g/AwlDgjAZAt6PPcXkfCJ6p2CQ=="],
"@storybook/docs-tools": ["@storybook/docs-tools@8.5.1", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-0mPWEnYhAMYeSGWKK9NNbdWyDMIy9HB3cPdY6NKkvHcw7Sy9cmQtvuvwVPptjdcf0BShJkMTJjmf5h9YNhJPmQ=="],
"@storybook/docs-tools": ["@storybook/docs-tools@8.5.0", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-W5C0Gu6FTqQaXGMYtlPNSPgRHbGS9bcvGRWxBA3+V6LSxL4he/yegwz/AH5dfLVI2PCHVedNtIv+yHKM+a2E3w=="],
"@storybook/global": ["@storybook/global@5.0.0", "", {}, "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ=="],
"@storybook/icons": ["@storybook/icons@1.3.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" } }, "sha512-t3xcbCKkPvqyef8urBM0j/nP6sKtnlRkVgC+8JTbTAZQjaTmOjes3byEgzs89p4B/K6cJsg9wLW2k3SknLtYJw=="],
"@storybook/icons": ["@storybook/icons@1.3.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" } }, "sha512-Nz/UzeYQdUZUhacrPyfkiiysSjydyjgg/p0P9HxB4p/WaJUUjMAcaoaLgy3EXx61zZJ3iD36WPuDkZs5QYrA0A=="],
"@storybook/instrumenter": ["@storybook/instrumenter@8.5.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "@vitest/utils": "^2.1.1" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-wMAhsIzwOh/xXKANAP3IbtXxRWFAZtpRisB0sy8WVTPS3a1L1cA6X+U80Ex/omek6L0FZwKZSKmmfkDeZkYnCQ=="],
"@storybook/instrumenter": ["@storybook/instrumenter@8.5.0", "", { "dependencies": { "@storybook/global": "^5.0.0", "@vitest/utils": "^2.1.1" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-eZ/UY6w4U2vay+wX7QVwKiRoyMzZscuv6v4k4r8BlmHPFWbhiZDO9S2GsG16UkyKnrQrYk432he70n7hn1Xvmg=="],
"@storybook/manager-api": ["@storybook/manager-api@8.5.1", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-Oj9kPYbp/82LRQ+rsc0ZH0fkzeiT2U1kvubmNiRjtopQHCP3UTVnvWIXC9zSRFKmS+NaAdd0JYsIBvE8fjnoqQ=="],
"@storybook/manager-api": ["@storybook/manager-api@8.5.0", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-Ildriueo3eif4M+gMlMxu/mrBIbAnz8+oesmQJKdzZfe/U9eQTI9OUqJsxx/IVBmdzQ3ySsgNmzj5VweRkse4A=="],
"@storybook/node-logger": ["@storybook/node-logger@8.5.1", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-O12j4IdpaM5Z/CCWU5NyS/6vALFc5vChwfsWEWFfE3lOUMfLHYuQgH+x5XE6Ctq53ua2IHGOZAmwLrrpxVx4dg=="],
"@storybook/node-logger": ["@storybook/node-logger@8.5.0", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-dQqFZeT7AnRRaHNNicQWnuG+dNJnEFLxiYpNg10veqjWaRBvLOInoawyVWHhhezaBbde7nXg10mWZ8CMlPfevQ=="],
"@storybook/preview-api": ["@storybook/preview-api@8.5.1", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-fLR7nvAbjHVLazDA6CLy9O/bpBzKDKqxyBp6SybTBPYa76IzsX8ITSMMt1YcP6rOGhVgcKNA9iBNxRddjLIV0Q=="],
"@storybook/preview-api": ["@storybook/preview-api@8.5.0", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-g0XbD54zMUkl6bpuA7qEBCE9rW1QV6KKmwkO4bkxMOJcMke3x9l00JTaYn7Un8wItjXiS3BIG15B6mnfBG7fng=="],
"@storybook/react-dom-shim": ["@storybook/react-dom-shim@8.5.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^8.5.1" } }, "sha512-peDiT6A1zyODKd7tVQIiFNU42Iolca67h3kkOQPb7nm/Czf2yIa/BHw+yiNDZx82eCIEvBy1Xf7lnjH8PD61xA=="],
"@storybook/react-dom-shim": ["@storybook/react-dom-shim@8.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^8.5.0" } }, "sha512-7P8xg4FiuFpM6kQOzZynno+0zyLVs8NgsmRK58t3JRZXbda1tzlxTXzvqx4hUevvbPJGjmrB0F3xTFH+8Otnvw=="],
"@storybook/svelte": ["@storybook/svelte@8.5.1", "", { "dependencies": { "@storybook/components": "8.5.1", "@storybook/global": "^5.0.0", "@storybook/manager-api": "8.5.1", "@storybook/preview-api": "8.5.1", "@storybook/theming": "8.5.1", "sveltedoc-parser": "^4.2.1", "ts-dedent": "^2.0.0", "type-fest": "~2.19" }, "peerDependencies": { "storybook": "^8.5.1", "svelte": "^4.0.0 || ^5.0.0" } }, "sha512-DtApr2+wCKVp5kx/K9a55bzVVfypTa26xU4SC7UvjhKmvein/BNNilhgUh9th4wZApipNYKsfdXYDQBwzSYAOw=="],
"@storybook/svelte": ["@storybook/svelte@8.5.0", "", { "dependencies": { "@storybook/components": "8.5.0", "@storybook/global": "^5.0.0", "@storybook/manager-api": "8.5.0", "@storybook/preview-api": "8.5.0", "@storybook/theming": "8.5.0", "sveltedoc-parser": "^4.2.1", "ts-dedent": "^2.0.0", "type-fest": "~2.19" }, "peerDependencies": { "storybook": "^8.5.0", "svelte": "^4.0.0 || ^5.0.0" } }, "sha512-P3qG441QgSk6fOXcMT/PvkLcds6fX36PcxY5CpV+xDwaxtx6ghV+2FZL85scdgX+EeihJ9xnNiUGLapK9Pltuw=="],
"@storybook/svelte-vite": ["@storybook/svelte-vite@8.5.1", "", { "dependencies": { "@storybook/builder-vite": "8.5.1", "@storybook/svelte": "8.5.1", "magic-string": "^0.30.0", "svelte-preprocess": "^5.1.1", "svelte2tsx": "^0.7.13", "sveltedoc-parser": "^4.2.1", "ts-dedent": "^2.2.0", "typescript": "^4.9.4 || ^5.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "storybook": "^8.5.1", "svelte": "^4.0.0 || ^5.0.0", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-hPDbkZvvvgxIqua48zep5JZFMo/cOl/vJ9n2A5iI5FMloxxkQ/aJFmJu80VvChaGC3D+lwZdz+vhiK+ZoPqaSQ=="],
"@storybook/svelte-vite": ["@storybook/svelte-vite@8.5.0", "", { "dependencies": { "@storybook/builder-vite": "8.5.0", "@storybook/svelte": "8.5.0", "magic-string": "^0.30.0", "svelte-preprocess": "^5.1.1", "svelte2tsx": "^0.7.13", "sveltedoc-parser": "^4.2.1", "ts-dedent": "^2.2.0", "typescript": "^4.9.4 || ^5.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "storybook": "^8.5.0", "svelte": "^4.0.0 || ^5.0.0", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-lHo7txeqXZh93oXBXZi5ZIQ3m5CyWNEPatnbyBq06oLlCwNSdhUC4+ex3lTiqTY/aitQXf/BkRiKJfVNneB2jw=="],
"@storybook/sveltekit": ["@storybook/sveltekit@8.5.1", "", { "dependencies": { "@storybook/addon-actions": "8.5.1", "@storybook/builder-vite": "8.5.1", "@storybook/svelte": "8.5.1", "@storybook/svelte-vite": "8.5.1" }, "peerDependencies": { "storybook": "^8.5.1", "svelte": "^4.0.0 || ^5.0.0", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-XI0Nq9SjMErCaEPx30S8MrUK0N50k58iaig6Gg/cLkBE1VmBjdZqMynpjkm7/dU4/iUis9vNg0st8nOXOIsjUg=="],
"@storybook/sveltekit": ["@storybook/sveltekit@8.5.0", "", { "dependencies": { "@storybook/addon-actions": "8.5.0", "@storybook/builder-vite": "8.5.0", "@storybook/svelte": "8.5.0", "@storybook/svelte-vite": "8.5.0" }, "peerDependencies": { "storybook": "^8.5.0", "svelte": "^4.0.0 || ^5.0.0", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-Gm9elJTiFDrECkkJzKPygyHwKzjtkaBDeilHfH7UNsrgjP8JlNnQeRybVKWGy2lrVUB8yGJfEKys+qELYSIo3A=="],
"@storybook/test": ["@storybook/test@8.5.1", "", { "dependencies": { "@storybook/csf": "0.1.12", "@storybook/global": "^5.0.0", "@storybook/instrumenter": "8.5.1", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.5.0", "@testing-library/user-event": "14.5.2", "@vitest/expect": "2.0.5", "@vitest/spy": "2.0.5" }, "peerDependencies": { "storybook": "^8.5.1" } }, "sha512-V0sEXqL5kS0YKugCqWgmCpNODdlCCiVlPqm3i+E2+G97DR980BwXf8J6VPscQDRS9ZG39BrM83Aau6Anxrt1Tg=="],
"@storybook/test": ["@storybook/test@8.5.0", "", { "dependencies": { "@storybook/csf": "0.1.12", "@storybook/global": "^5.0.0", "@storybook/instrumenter": "8.5.0", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.5.0", "@testing-library/user-event": "14.5.2", "@vitest/expect": "2.0.5", "@vitest/spy": "2.0.5" }, "peerDependencies": { "storybook": "^8.5.0" } }, "sha512-M/DdPlI6gwL7NGkK5o7GYjdEBp95AsFEUtW29zQfnVIAngYugzi3nIuM/XkQHunidVdAZCYjw2s2Yhhsx/m9sw=="],
"@storybook/theming": ["@storybook/theming@8.5.1", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-sg61vY1gM8w42CIi28vo//6E1gHgHLNBNaRhkfvLFpu9PuhAcVWLwBDZq0BoKmDMxRxbSPV2gvIKeXdOtbSCJw=="],
"@storybook/theming": ["@storybook/theming@8.5.0", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-591LbOj/HMmHYUfLgrMerxhF1A9mY61HWKxcRpB6xxalc1Xw1kRtQ49DcwuTXnUu9ktBB3nuOzPNPQPFSh/7PQ=="],
"@storybook/types": ["@storybook/types@8.5.1", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-bD5KZ9628licnzMX4pFwWv6opxh3UTZr4zBO796Mm1lMEcGIqs9qFHDbbxly5tuV6IvI6nOSnm7djAFlba7YgQ=="],
"@storybook/types": ["@storybook/types@8.5.0", "", { "peerDependencies": { "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "sha512-5orPpfLvSksGH341ibmpFnV86yqlZMbvNax+a+z1h56UKRA+4c/hgdRQl1brs7YaQzrgJ2wUX7PAlJjBJ1erUQ=="],
"@sveltejs/adapter-node": ["@sveltejs/adapter-node@5.2.12", "", { "dependencies": { "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.9.5" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ=="],
"@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@3.3.1", "", { "dependencies": { "import-meta-resolve": "^4.1.0" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-5Sc7WAxYdL6q9j/+D0jJKjGREGlfIevDyHSQ2eNETHcB1TKlQWHcAo8AS8H1QdjNvSXpvOwNjykDUHPEAyGgdQ=="],
"@sveltejs/kit": ["@sveltejs/kit@2.16.1", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-2pF5sgGJx9brYZ/9nNDYnh5KX0JguPF14dnvvtf/MqrvlWrDj/e7Rk3LBJPecFLLK1GRs6ZniD24gFPqZm/NFw=="],
"@sveltejs/kit": ["@sveltejs/kit@2.15.3", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0", "tiny-glob": "^0.2.9" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-yI1iF1ldC+nyMFuA1cV+IMOROaGu6ogW0WNlTuPU5/3tGk8pQlwOtlbguGY4Fsp8Qf6pQWmG/ZUXRDrhAt62dg=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.0.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.0", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.15", "vitefu": "^1.0.4" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw=="],
@ -463,7 +455,7 @@
"@types/btoa-lite": ["@types/btoa-lite@1.0.2", "", {}, "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="],
"@types/bun": ["@types/bun@1.2.0", "", { "dependencies": { "bun-types": "1.2.0" } }, "sha512-5N1JqdahfpBlAv4wy6svEYcd/YfO2GNrbL95JOmFx8nkE6dbK4R0oSE5SpBA4vBRqgrOUAXF8Dpiz+gi7r80SA=="],
"@types/bun": ["@types/bun@1.1.16", "", { "dependencies": { "bun-types": "1.1.43" } }, "sha512-E+ue6NMcn4FXC5bDRE1W/BXUVs01h5Mt02qH8/8HGCox9akuh8KNOFdwvaQS9TDgT2RmUyJYFRRqA60WtTnm2g=="],
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
@ -475,39 +467,35 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.8", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg=="],
"@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg=="],
"@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="],
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
"@types/node": ["@types/node@22.10.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww=="],
"@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
"@types/pug": ["@types/pug@2.0.10", "", {}, "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA=="],
"@types/react": ["@types/react@19.0.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw=="],
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
"@types/react": ["@types/react@19.0.7", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA=="],
"@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="],
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
"@types/ws": ["@types/ws@8.5.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.21.0", "@typescript-eslint/type-utils": "8.21.0", "@typescript-eslint/utils": "8.21.0", "@typescript-eslint/visitor-keys": "8.21.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.20.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.20.0", "@typescript-eslint/type-utils": "8.20.0", "@typescript-eslint/utils": "8.20.0", "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.21.0", "@typescript-eslint/types": "8.21.0", "@typescript-eslint/typescript-estree": "8.21.0", "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.20.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.20.0", "@typescript-eslint/types": "8.20.0", "@typescript-eslint/typescript-estree": "8.20.0", "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.21.0", "", { "dependencies": { "@typescript-eslint/types": "8.21.0", "@typescript-eslint/visitor-keys": "8.21.0" } }, "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.20.0", "", { "dependencies": { "@typescript-eslint/types": "8.20.0", "@typescript-eslint/visitor-keys": "8.20.0" } }, "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.21.0", "@typescript-eslint/utils": "8.21.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.20.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.20.0", "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.21.0", "", {}, "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.20.0", "", {}, "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.21.0", "", { "dependencies": { "@typescript-eslint/types": "8.21.0", "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.20.0", "", { "dependencies": { "@typescript-eslint/types": "8.20.0", "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.21.0", "@typescript-eslint/types": "8.21.0", "@typescript-eslint/typescript-estree": "8.21.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.20.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.20.0", "@typescript-eslint/types": "8.20.0", "@typescript-eslint/typescript-estree": "8.20.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.21.0", "", { "dependencies": { "@typescript-eslint/types": "8.21.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.20.0", "", { "dependencies": { "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA=="],
"@vitest/expect": ["@vitest/expect@2.0.5", "", { "dependencies": { "@vitest/spy": "2.0.5", "@vitest/utils": "2.0.5", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" } }, "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA=="],
@ -647,7 +635,7 @@
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"bun-types": ["bun-types@1.2.0", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-KEaJxyZfbV/c4eyG0vyehDpYmBGreNiQbZIqvVHJwZ4BmeuWlNZ7EAzMN2Zcd7ailmS/tGVW0BgYbGf+lGEpWw=="],
"bun-types": ["bun-types@1.1.43", "", { "dependencies": { "@types/node": "~20.12.8", "@types/ws": "~8.5.10" } }, "sha512-W0wCtVH+bwFp7p3Zgs03CqxEDmXxEvmmUM/FBKgWIv9T8gyeotvIjIbHzuDScc2DphhRNtr7hJLCR5PspYL5qw=="],
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
@ -663,7 +651,7 @@
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
"caniuse-lite": ["caniuse-lite@1.0.30001695", "", {}, "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw=="],
"caniuse-lite": ["caniuse-lite@1.0.30001692", "", {}, "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A=="],
"chai": ["chai@5.1.2", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw=="],
@ -673,7 +661,7 @@
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"chromatic": ["chromatic@11.25.1", "", { "peerDependencies": { "@chromatic-com/cypress": "^0.*.* || ^1.0.0", "@chromatic-com/playwright": "^0.*.* || ^1.0.0" }, "optionalPeers": ["@chromatic-com/cypress", "@chromatic-com/playwright"], "bin": { "chroma": "dist/bin.js", "chromatic": "dist/bin.js", "chromatic-cli": "dist/bin.js" } }, "sha512-D0NdcGOSy84hqgNnSY7FM4TzB77RymRTowjm4hb1CV4wbk1djKTV4SJbbYVCzHFD+n/NOg/wtZ9Y7sjiRdy8dA=="],
"chromatic": ["chromatic@11.25.0", "", { "peerDependencies": { "@chromatic-com/cypress": "^0.*.* || ^1.0.0", "@chromatic-com/playwright": "^0.*.* || ^1.0.0" }, "optionalPeers": ["@chromatic-com/cypress", "@chromatic-com/playwright"], "bin": { "chroma": "dist/bin.js", "chromatic": "dist/bin.js", "chromatic-cli": "dist/bin.js" } }, "sha512-P2BVe0rRLS9WM+eSG3u1SRg0Mi2vopsdPs2FiXwUiPqZ6hs9fe66d3Pnt7CfQ22v2jThuPEXYjYEeuL75a16Bw=="],
"chrome-trace-event": ["chrome-trace-event@1.0.4", "", {}, "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ=="],
@ -697,12 +685,10 @@
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="],
"commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
"comment-json": ["comment-json@4.2.5", "", { "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", "esprima": "^4.0.1", "has-own-prop": "^2.0.0", "repeat-string": "^1.6.1" } }, "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw=="],
"commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"consola": ["consola@3.2.3", "", {}, "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ=="],
@ -787,7 +773,7 @@
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
"electron-to-chromium": ["electron-to-chromium@1.5.88", "", {}, "sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw=="],
"electron-to-chromium": ["electron-to-chromium@1.5.83", "", {}, "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ=="],
"emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
@ -821,7 +807,7 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.19.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.19.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA=="],
"eslint": ["eslint@9.18.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.18.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA=="],
"eslint-compat-utils": ["eslint-compat-utils@0.5.1", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q=="],
@ -851,7 +837,7 @@
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
@ -877,13 +863,13 @@
"fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="],
"fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="],
"fast-uri": ["fast-uri@3.0.5", "", {}, "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q=="],
"fastparse": ["fastparse@1.1.2", "", {}, "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ=="],
"fastq": ["fastq@1.18.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw=="],
"fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
"fdir": ["fdir@6.4.2", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
@ -899,7 +885,7 @@
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"for-each": ["for-each@0.3.4", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw=="],
"for-each": ["for-each@0.3.3", "", { "dependencies": { "is-callable": "^1.1.3" } }, "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw=="],
"foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="],
@ -931,6 +917,10 @@
"globals": ["globals@15.14.0", "", {}, "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig=="],
"globalyzer": ["globalyzer@0.1.0", "", {}, "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q=="],
"globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
@ -997,8 +987,6 @@
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
@ -1059,7 +1047,7 @@
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
"lint-staged": ["lint-staged@15.4.3", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0", "debug": "^4.4.0", "execa": "^8.0.1", "lilconfig": "^3.1.3", "listr2": "^8.2.5", "micromatch": "^4.0.8", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.7.0" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g=="],
"lint-staged": ["lint-staged@15.4.1", "", { "dependencies": { "chalk": "~5.4.1", "commander": "~12.1.0", "debug": "~4.4.0", "execa": "~8.0.1", "lilconfig": "~3.1.3", "listr2": "~8.2.5", "micromatch": "~4.0.8", "pidtree": "~0.6.0", "string-argv": "~0.3.2", "yaml": "~2.6.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-P8yJuVRyLrm5KxCtFx+gjI5Bil+wO7wnTl7C3bXhvtTaAFGirzeB24++D0wGoUwxrUKecNiehemgCob9YL39NA=="],
"listr2": ["listr2@8.2.5", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ=="],
@ -1209,7 +1197,7 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
@ -1225,9 +1213,9 @@
"pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="],
"playwright": ["playwright@1.50.0", "", { "dependencies": { "playwright-core": "1.50.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-+GinGfGTrd2IfX1TA4N2gNmeIksSb+IAe589ZH+FlmpV3MYTx6+buChGIuDLQwrGNCw2lWibqV50fU510N7S+w=="],
"playwright": ["playwright@1.49.1", "", { "dependencies": { "playwright-core": "1.49.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA=="],
"playwright-core": ["playwright-core@1.50.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-CXkSSlr4JaZs2tZHI40DsZUN/NIwgaUPsyLuOAaIZp2CyF2sN5MM5NJsyB188lFSSozFxQ5fPT4qM+f0tH/6wQ=="],
"playwright-core": ["playwright-core@1.49.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg=="],
"polished": ["polished@4.3.1", "", { "dependencies": { "@babel/runtime": "^7.17.8" } }, "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA=="],
@ -1251,7 +1239,7 @@
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
"posthog-node": ["posthog-node@4.4.1", "", { "dependencies": { "axios": "^1.7.4" } }, "sha512-o9G9sSvwWITrfSJgIUrPLJd//AYPGJNu5D+pSLxqiBvhUeicc/i639FvU0DPr1OsHiLDE2zHNMmLpa0mw4kBCg=="],
"posthog-node": ["posthog-node@4.4.0", "", { "dependencies": { "axios": "^1.7.4" } }, "sha512-4FEFBuc4FDFkTEiCSIo7DFV0jpprlqfR/SzjVgg7E2rRvfxTW4J47IvCwSCo5C9lBeV0ujDubY/YArHG37aJIw=="],
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
@ -1259,11 +1247,11 @@
"prettier-plugin-svelte": ["prettier-plugin-svelte@3.3.3", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw=="],
"prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.11", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA=="],
"prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.10", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-ndj2WLDaMzACnr1gAYZiZZLs5ZdOeBYgOsbBmHj3nvW/6q8h8PymsXiEnKvj/9qgCCAoHyvLOisoQdIcsDvIgw=="],
"pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
"prisma": ["prisma@6.3.1", "", { "dependencies": { "@prisma/engines": "6.3.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-JKCZWvBC3enxk51tY4TWzS4b5iRt4sSU1uHn2I183giZTvonXaQonzVtjLzpOHE7qu9MxY510kAtFGJwryKe3Q=="],
"prisma": ["prisma@6.2.1", "", { "dependencies": { "@prisma/engines": "6.2.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "bin": { "prisma": "build/index.js" } }, "sha512-hhyM0H13pQleQ+br4CkzGizS5I0oInoeTw3JfLw1BRZduBSQxPILlJLwi+46wZzj9Je7ndyQEMGw/n5cN2fknA=="],
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
@ -1323,7 +1311,7 @@
"rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"rollup": ["rollup@4.32.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.32.0", "@rollup/rollup-android-arm64": "4.32.0", "@rollup/rollup-darwin-arm64": "4.32.0", "@rollup/rollup-darwin-x64": "4.32.0", "@rollup/rollup-freebsd-arm64": "4.32.0", "@rollup/rollup-freebsd-x64": "4.32.0", "@rollup/rollup-linux-arm-gnueabihf": "4.32.0", "@rollup/rollup-linux-arm-musleabihf": "4.32.0", "@rollup/rollup-linux-arm64-gnu": "4.32.0", "@rollup/rollup-linux-arm64-musl": "4.32.0", "@rollup/rollup-linux-loongarch64-gnu": "4.32.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.32.0", "@rollup/rollup-linux-riscv64-gnu": "4.32.0", "@rollup/rollup-linux-s390x-gnu": "4.32.0", "@rollup/rollup-linux-x64-gnu": "4.32.0", "@rollup/rollup-linux-x64-musl": "4.32.0", "@rollup/rollup-win32-arm64-msvc": "4.32.0", "@rollup/rollup-win32-ia32-msvc": "4.32.0", "@rollup/rollup-win32-x64-msvc": "4.32.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-JmrhfQR31Q4AuNBjjAX4s+a/Pu/Q8Q9iwjWBsjRH1q52SPFE2NqRMK6fUZKKnvKO6id+h7JIRf0oYsph53eATg=="],
"rollup": ["rollup@4.30.1", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.30.1", "@rollup/rollup-android-arm64": "4.30.1", "@rollup/rollup-darwin-arm64": "4.30.1", "@rollup/rollup-darwin-x64": "4.30.1", "@rollup/rollup-freebsd-arm64": "4.30.1", "@rollup/rollup-freebsd-x64": "4.30.1", "@rollup/rollup-linux-arm-gnueabihf": "4.30.1", "@rollup/rollup-linux-arm-musleabihf": "4.30.1", "@rollup/rollup-linux-arm64-gnu": "4.30.1", "@rollup/rollup-linux-arm64-musl": "4.30.1", "@rollup/rollup-linux-loongarch64-gnu": "4.30.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1", "@rollup/rollup-linux-riscv64-gnu": "4.30.1", "@rollup/rollup-linux-s390x-gnu": "4.30.1", "@rollup/rollup-linux-x64-gnu": "4.30.1", "@rollup/rollup-linux-x64-musl": "4.30.1", "@rollup/rollup-win32-arm64-msvc": "4.30.1", "@rollup/rollup-win32-ia32-msvc": "4.30.1", "@rollup/rollup-win32-x64-msvc": "4.30.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
@ -1397,7 +1385,7 @@
"std-env": ["std-env@3.8.0", "", {}, "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w=="],
"storybook": ["storybook@8.5.1", "", { "dependencies": { "@storybook/core": "8.5.1" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": { "sb": "./bin/index.cjs", "storybook": "./bin/index.cjs", "getstorybook": "./bin/index.cjs" } }, "sha512-HuaAFA97j2w4i/1EHKj6X4iDiVzPrXzQpmTEE1tLD1QXzqrQKKHse+Ggc8AGMuLTAzxA6xmrX9xibgMNWCgvRA=="],
"storybook": ["storybook@8.5.0", "", { "dependencies": { "@storybook/core": "8.5.0" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": { "sb": "./bin/index.cjs", "storybook": "./bin/index.cjs", "getstorybook": "./bin/index.cjs" } }, "sha512-cEx42OlCetManF+cONVJVYP7SYsnI2K922DfWKmZhebP0it0n6TUof4y5/XzJ8YUruwPgyclGLdX8TvdRuNSfw=="],
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
@ -1423,7 +1411,7 @@
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"svelte": ["svelte@5.19.3", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "acorn-typescript": "^1.4.13", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.3", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-rb/bkYG9jq67OCWikMvaPnfOobyGn0JizVDwHpdeBtLiNXPMcoA9GTFC3BhptP7xGNquUU8J5GiS7PlGlfDAFA=="],
"svelte": ["svelte@5.18.0", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "acorn-typescript": "^1.4.13", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.3", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-/Eb81lB8bVUxQPmkPVNBYrU9cZ544+9hE91ZUUXTMf7eWcGW84N1hS3gvv/XsUNOWLLg3IicXP2qa8W3KpTUHA=="],
"svelte-ast-print": ["svelte-ast-print@0.4.2", "", { "dependencies": { "esrap": "1.2.2", "zimmerframe": "1.1.2" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-hRHHufbJoArFmDYQKCpCvc0xUuIEfwYksvyLYEQyH+1xb5LD5sM/IthfooCdXZQtOIqXz6xm7NmaqdfwG4kh6w=="],
@ -1459,6 +1447,8 @@
"throttle-debounce": ["throttle-debounce@5.0.2", "", {}, "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A=="],
"tiny-glob": ["tiny-glob@0.2.9", "", { "dependencies": { "globalyzer": "0.1.0", "globrex": "^0.1.2" } }, "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg=="],
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
@ -1493,9 +1483,9 @@
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
"typescript-eslint": ["typescript-eslint@8.21.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.21.0", "@typescript-eslint/parser": "8.21.0", "@typescript-eslint/utils": "8.21.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw=="],
"typescript-eslint": ["typescript-eslint@8.20.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.20.0", "@typescript-eslint/parser": "8.20.0", "@typescript-eslint/utils": "8.20.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA=="],
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"universal-github-app-jwt": ["universal-github-app-jwt@1.2.0", "", { "dependencies": { "@types/jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.2" } }, "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g=="],
@ -1523,7 +1513,7 @@
"value-or-promise": ["value-or-promise@1.0.12", "", {}, "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q=="],
"vite": ["vite@6.0.11", "", { "dependencies": { "esbuild": "^0.24.2", "postcss": "^8.4.49", "rollup": "^4.23.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg=="],
"vite": ["vite@6.0.7", "", { "dependencies": { "esbuild": "^0.24.2", "postcss": "^8.4.49", "rollup": "^4.23.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ=="],
"vite-node": ["vite-node@2.1.8", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg=="],
@ -1557,7 +1547,7 @@
"xmlbuilder": ["xmlbuilder@13.0.2", "", {}, "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ=="],
"yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="],
"yaml": ["yaml@2.6.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
@ -1595,8 +1585,6 @@
"@inlang/paraglide-js/dedent": ["dedent@1.5.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="],
"@inlang/paraglide-sveltekit/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
"@inlang/paraglide-sveltekit/dedent": ["dedent@1.5.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="],
"@inlang/paraglide-sveltekit/devalue": ["devalue@4.3.3", "", {}, "sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg=="],
@ -1639,30 +1627,26 @@
"@octokit/request-error/@octokit/types": ["@octokit/types@13.7.0", "", { "dependencies": { "@octokit/openapi-types": "^23.0.1" } }, "sha512-BXfRP+3P3IN6fd4uF3SniaHKOO4UXWBfkdR3vA8mIvaoO/wLjGN5qivUtW0QRitBHHMcfC41SLhNVYIZZE+wkA=="],
"@prisma/generator-helper/@prisma/debug": ["@prisma/debug@6.2.1", "", {}, "sha512-0KItvt39CmQxWkEw6oW+RQMD6RZ43SJWgEUnzxN8VC9ixMysa7MzZCZf22LCK5DSooiLNf8vM3LHZm/I/Ni7bQ=="],
"@storybook/blocks/@storybook/csf": ["@storybook/csf@0.1.12", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-9/exVhabisyIVL0VxTCxo01Tdm8wefIXKXfltAPTSr8cbLn5JAxGQ6QV3mjdecLGEOucfoVhAKtJfVHxEK1iqw=="],
"@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
"@storybook/core/@storybook/csf": ["@storybook/csf@0.1.12", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-9/exVhabisyIVL0VxTCxo01Tdm8wefIXKXfltAPTSr8cbLn5JAxGQ6QV3mjdecLGEOucfoVhAKtJfVHxEK1iqw=="],
"@storybook/test/@storybook/csf": ["@storybook/csf@0.1.12", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-9/exVhabisyIVL0VxTCxo01Tdm8wefIXKXfltAPTSr8cbLn5JAxGQ6QV3mjdecLGEOucfoVhAKtJfVHxEK1iqw=="],
"@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="],
"@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
"@testing-library/jest-dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
"@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
"@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="],
"@types/react/csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"@vitest/expect/@vitest/utils": ["@vitest/utils@2.0.5", "", { "dependencies": { "@vitest/pretty-format": "2.0.5", "estree-walker": "^3.0.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" } }, "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ=="],
"@vitest/mocker/@vitest/spy": ["@vitest/spy@2.1.8", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg=="],
"@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"@whatwg-node/disposablestack/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@whatwg-node/events/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
@ -1673,8 +1657,6 @@
"ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"ast-types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"cross-inspect/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
@ -1683,6 +1665,8 @@
"domutils/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="],
"dot-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="],
@ -1699,7 +1683,9 @@
"log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"lower-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"no-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
@ -1727,7 +1713,7 @@
"slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"solid-js/csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"snake-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
@ -1759,13 +1745,13 @@
"terser-webpack-plugin/schema-utils": ["schema-utils@4.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", "ajv-formats": "^2.1.1", "ajv-keywords": "^5.1.0" } }, "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g=="],
"vite-node/vite": ["vite@5.4.14", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA=="],
"vite-node/vite": ["vite@5.4.11", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q=="],
"vitest/@vitest/expect": ["@vitest/expect@2.1.8", "", { "dependencies": { "@vitest/spy": "2.1.8", "@vitest/utils": "2.1.8", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw=="],
"vitest/@vitest/spy": ["@vitest/spy@2.1.8", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg=="],
"vitest/vite": ["vite@5.4.14", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA=="],
"vitest/vite": ["vite@5.4.11", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q=="],
"webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
@ -1809,8 +1795,6 @@
"@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@2.0.5", "", { "dependencies": { "tinyrainbow": "^1.2.0" } }, "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ=="],
"@vitest/expect/@vitest/utils/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"enquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
@ -1871,8 +1855,6 @@
"sveltedoc-parser/eslint/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"terser-webpack-plugin/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"vite-node/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],

View file

@ -2,7 +2,6 @@
"$schema": "https://inlang.com/schema/inlang-message-format",
"nav_greeting": "Hello {name}!",
"nav_menu_sms": "SMS",
"nav_menu_residents": "Residents",
"nav_menu_settings": "Settings",
"nav_menu_logout": "Logout",
"login_tab_login": "Login",
@ -22,20 +21,5 @@
"sms_label_phone": "Phone Number",
"sms_label_message": "Message",
"sms_button_submit": "Send Message",
"residents_title": "Residents",
"residents_button_new": "New Resident",
"residents_table_edit": "Edit",
"residents_modal_title_new": "Create a Resident",
"residents_modal_title_edit": "Edit Resident",
"residents_modal_submit": "Submit",
"residents_modal_delete": "Delete",
"residents_modal_label_name": "Name",
"residents_modal_label_phone": "Phone Number",
"settings_title": "Settings",
"settings_category_twilio": "Twilio Config",
"settings_twilio_account_sid": "Account SID",
"settings_twilio_auth_token": "Auth Token",
"settings_twilio_phone_number": "Phone Number",
"settings_save": "Save Settings",
"error_page_go_home": "Go Home"
}

3
netlify.toml Normal file
View file

@ -0,0 +1,3 @@
[build]
command = "bun run build"
publish = "build"

View file

@ -3,20 +3,16 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"prepare": "husky",
"postinstall": "prisma generate",
"dev": "bun validate-env && bun database:up && bun prisma:generate && vite dev",
"build": "vite build",
"build-storybook": "storybook build",
"database:up": "docker compose -p hestia -f devops/docker-compose.dev.yml up -d && docker compose -p hestia -f devops/docker-compose.dev.yml -f devops/docker-compose.wait.yml run --rm wait -c hestia-database:5432 && bun prisma:push",
"database:down": "docker compose -p hestia -f devops/docker-compose.dev.yml down",
"docker:build": "docker buildx -t hestia .",
"docker:build-storybook": "docker buildx -t hestia-storybook -f Dockerfile.storybook .",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"generate-secret": "bun ./scripts/generate-secret.ts",
"lint": "prettier --check . && eslint .",
"test:unit": "vitest",
"test": "bun run test:unit -- --run && bun run test:e2e",
@ -29,25 +25,26 @@
"prisma:reset": "prisma migrate reset --force",
"prisma:studio": "prisma studio",
"prisma:validate": "prisma validate",
"prepare": "husky",
"validate-env": "bun ./scripts/validate-env.ts"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.2",
"@eslint/compat": "^1.2.3",
"@playwright/test": "^1.45.3",
"@storybook/addon-essentials": "^8.5.0",
"@storybook/addon-interactions": "^8.5.0",
"@storybook/addon-essentials": "^8.4.7",
"@storybook/addon-interactions": "^8.4.7",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-svelte-csf": "^5.0.0-next.13",
"@storybook/addon-themes": "^8.5.0",
"@storybook/blocks": "^8.5.0",
"@storybook/svelte": "^8.5.0",
"@storybook/sveltekit": "^8.5.0",
"@storybook/test": "^8.5.0",
"@sveltejs/adapter-node": "^5.2.12",
"@storybook/addon-themes": "^8.4.7",
"@storybook/blocks": "^8.4.7",
"@storybook/svelte": "^8.4.7",
"@storybook/sveltekit": "^8.4.7",
"@storybook/test": "^8.4.7",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.15.3",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@types/bun": "^1.1.15",
"@types/bun": "^1.1.14",
"autoprefixer": "^10.4.20",
"daisyui": "^4.12.22",
"eslint": "^9.7.0",
@ -59,8 +56,8 @@
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.6",
"prettier-plugin-tailwindcss": "^0.6.5",
"prisma": "6.3.1",
"storybook": "^8.5.0",
"prisma": "^6.0.1",
"storybook": "^8.4.7",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.9",
@ -75,7 +72,7 @@
"@inlang/paraglide-sveltekit": "^0.15.0",
"@pothos/core": "^4.3.0",
"@pothos/plugin-prisma": "^4.4.0",
"@prisma/client": "6.3.1",
"@prisma/client": "6.0.1",
"@tailwindcss/typography": "^0.5.15",
"clerk-sveltekit": "https://pkg.pr.new/wobsoriano/clerk-sveltekit@ca15d4e",
"clsx": "^2.1.1",

View file

@ -1,34 +0,0 @@
-- CreateTable
CREATE TABLE "TenantConfig" (
"id" TEXT NOT NULL,
"tenantId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "TenantConfig_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TwilioConfig" (
"id" TEXT NOT NULL,
"tenantConfigId" TEXT NOT NULL,
"accountSID" TEXT NOT NULL,
"authToken" TEXT NOT NULL,
"phoneNumber" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "TwilioConfig_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "TenantConfig_tenantId_key" ON "TenantConfig"("tenantId");
-- CreateIndex
CREATE UNIQUE INDEX "TwilioConfig_tenantConfigId_key" ON "TwilioConfig"("tenantConfigId");
-- AddForeignKey
ALTER TABLE "TenantConfig" ADD CONSTRAINT "TenantConfig_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TwilioConfig" ADD CONSTRAINT "TwilioConfig_tenantConfigId_fkey" FOREIGN KEY ("tenantConfigId") REFERENCES "TenantConfig"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -1,14 +0,0 @@
-- CreateTable
CREATE TABLE "Resident" (
"id" TEXT NOT NULL,
"tenantId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"phoneNumber" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Resident_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Resident" ADD CONSTRAINT "Resident_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

View file

@ -10,9 +10,8 @@ generator pothos {
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
@ -31,53 +30,12 @@ model User {
@@unique([clerkId, tenantId])
}
model Resident {
id String @id @default(uuid())
tenant Tenant @relation(fields: [tenantId], references: [id])
tenantId String
name String
phoneNumber String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Tenant {
id String @id @default(uuid())
clerkOrganizationId String @unique
users User[]
residents Resident[]
tenantConfig TenantConfig?
name String
slug String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id String @id @default(uuid())
name String
slug String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clerkOrganizationId String @unique
users User[]
}
model TenantConfig {
id String @id @default(uuid())
tenant Tenant @relation(fields: [tenantId], references: [id])
tenantId String @unique
twilioConfig TwilioConfig?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model TwilioConfig {
id String @id @default(uuid())
tenantConfig TenantConfig @relation(fields: [tenantConfigId], references: [id])
tenantConfigId String @unique
accountSID String
authToken String
phoneNumber String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

View file

@ -1,6 +0,0 @@
import { randomBytes } from 'node:crypto';
console.log('SECRET: ', {
password: randomBytes(16).toString('hex'),
salt: randomBytes(16).toString('hex'),
});

View file

@ -1,26 +1,12 @@
import { PhoneRegex } from '../src/lib/regex/phone';
import { z } from 'zod';
const ValidateEnvironment = () => {
const { success, error } = z
.object({
SECRETS_PASSWORD: z.string().length(32),
SECRETS_SALT: z.string().min(16),
SECRETS_IV_POSITION: z.string().transform((val, ctx) => {
const parsed = parseInt(val);
if (isNaN(parsed)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Not a number',
});
// This is a special symbol you can use to
// return early from the transform function.
// It has type `never` so it does not affect the
// inferred return type.
return z.NEVER;
}
return parsed;
}),
TWILIO_ACCOUNT_SID: z.string().min(1),
TWILIO_AUTH_TOKEN: z.string().min(1),
TWILIO_PHONE_NUMBER: z.string().regex(PhoneRegex),
})
.safeParse(process.env);

4
src/app.d.ts vendored
View file

@ -1,7 +1,5 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
import type { Tenant, User } from '@prisma/client';
// for information about these interfaces
declare global {
namespace App {
@ -12,8 +10,6 @@ declare global {
orgId?: string | null;
sessionId?: string;
};
user: User;
tenant: Tenant;
}
// interface PageData {}
// interface PageState {}

View file

@ -1,5 +1,3 @@
import { validateSession } from '$lib/server/middleware';
import { sequence } from '@sveltejs/kit/hooks';
import { withClerkHandler } from 'clerk-sveltekit/server';
export const handle = sequence(withClerkHandler(), validateSession());
export const handle = withClerkHandler();

View file

@ -24,6 +24,7 @@
children,
class: className,
color,
disabled,
full = false,
glass = false,
shape,
@ -36,6 +37,7 @@
<button
{...props}
{disabled}
class={twMerge('btn', clsx(className))}
class:btn-active={active}
class:no-animation={!animation}
@ -49,7 +51,7 @@
class:btn-success={color === 'success'}
class:btn-warning={color === 'warning'}
class:btn-error={color === 'error'}
class:btn-disabled={props.disabled}
class:btn-disabled={disabled}
class:w-full={full}
class:glass
class:btn-circle={shape === 'circle'}

View file

@ -1,36 +0,0 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import type { ComponentProps } from 'svelte';
import Modal from './Modal.svelte';
import ModalBody from './ModalBody.svelte';
import ModalActions from './ModalActions.svelte';
import Button from '../Button.svelte';
const { Story } = defineMeta({
title: 'Actions/Modal',
component: Modal,
argTypes: {
open: {
control: 'boolean',
defaultValue: false,
},
},
});
let dialog: HTMLDialogElement | undefined = $state(undefined);
</script>
{#snippet template({ children: _, ...props }: Partial<ComponentProps<typeof Modal>>)}
<Button onclick={() => dialog?.showModal()}>Open</Button>
<Modal {...props} backdrop bind:dialog>
<ModalBody>
<h3 class="text-lg font-bold">Hello!</h3>
<p class="py-4">Press ESC key or click the button below to close</p>
<ModalActions>
<Button onclick={() => dialog?.close()}>Close</Button>
</ModalActions>
</ModalBody>
</Modal>
{/snippet}
<Story name="Default" children={template} />

View file

@ -1,21 +0,0 @@
<script lang="ts">
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = SvelteHTMLElements['dialog'] & {
backdrop?: boolean;
dialog?: HTMLDialogElement;
};
let { backdrop, dialog = $bindable(), children, class: className, ...props }: Props = $props();
</script>
<dialog {...props} class={twMerge('modal', clsx(className))} bind:this={dialog}>
{@render children?.()}
{#if backdrop}
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
{/if}
</dialog>

View file

@ -1,13 +0,0 @@
<script lang="ts">
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = SvelteHTMLElements['div'];
let { children, class: className, ...props }: Props = $props();
</script>
<div {...props} class={twMerge('modal-action', clsx(className))}>
{@render children?.()}
</div>

View file

@ -1,13 +0,0 @@
<script lang="ts">
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = SvelteHTMLElements['div'];
let { children, class: className, ...props }: Props = $props();
</script>
<div {...props} class={twMerge('modal-box', clsx(className))}>
{@render children?.()}
</div>

View file

@ -1,3 +0,0 @@
export { default as Modal } from './Modal.svelte';
export { default as ModalActions } from './ModalActions.svelte';
export { default as ModalBody } from './ModalBody.svelte';

View file

@ -1,2 +1 @@
export { default as Button } from './Button.svelte';
export * from './Modal/';

View file

@ -38,7 +38,7 @@
<label class="form-control w-full" transition:fadeTransition={{ duration: fade ? 200 : 0 }}>
<div class="label">
<span
class="label-text flex items-center gap-2"
class="label-text"
class:text-primary={color === 'primary'}
class:text-secondary={color === 'secondary'}
class:text-accent={color === 'accent'}

View file

@ -1,34 +1,25 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { Snippet } from 'svelte';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
bordered?: boolean;
color?: Omit<DaisyColor, 'neutral'>;
disabled?: boolean;
error?: string | Snippet;
form?: string;
label?: string | Snippet;
resizable?: boolean | 'yes' | 'no' | 'x' | 'y';
size?: DaisySize | 'md';
} & SvelteHTMLElements['textarea'];
let {
bordered,
class: className,
color,
error,
label,
resizable,
size,
...props
}: Props = $props();
name?: string;
placeholder?: string;
size?: DaisySize;
};
let { bordered, color, error, label, size, ...props }: Props = $props();
</script>
<label class="form-control w-full">
<label class="form-control w-full max-w-lg">
<div class="label">
<span
class="label-text flex items-center gap-2"
class="label-text"
class:text-primary={color === 'primary'}
class:text-secondary={color === 'secondary'}
class:text-accent={color === 'accent'}
@ -52,8 +43,7 @@
</span>
</div>
<textarea
{...props}
class={twMerge('textarea', clsx(className))}
class="textarea"
class:textarea-bordered={bordered}
class:textarea-xs={size === 'xs'}
class:textarea-sm={size === 'sm'}
@ -66,9 +56,6 @@
class:textarea-success={color === 'success'}
class:textarea-warning={color === 'warning'}
class:textarea-error={color === 'error' || error}
class:resize={resizable === true || resizable === 'yes'}
class:resize-x={resizable === 'x'}
class:resize-y={resizable === 'y'}
class:resize-none={resizable === false || resizable === 'no'}
{...props}
></textarea>
</label>

View file

@ -1,51 +0,0 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Avatar from './Avatar.svelte';
const { Story } = defineMeta({
title: 'Data display/Avatar',
component: Avatar,
argTypes: {
img: {
control: 'text',
},
placeholder: {
control: 'text',
defaultValue: 'DP',
},
presence: {
control: 'select',
options: [undefined, 'online', 'offline'],
},
ring: {
control: 'select',
options: [
undefined,
'neutral',
'primary',
'secondary',
'accent',
'info',
'success',
'warning',
'error',
],
},
shape: {
control: 'select',
options: ['square', 'circle'],
},
size: {
control: 'select',
options: ['xs', 'sm', 'md', 'lg'],
},
},
});
</script>
<Story
name="Default"
args={{
img: 'https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp',
}}
/>

View file

@ -1,62 +0,0 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
img?: string;
placeholder: string;
presence?: 'online' | 'offline';
ring?: Exclude<DaisyColor, 'ghost'>;
shape?: 'square' | 'circle';
size?: DaisySize | 'md';
} & Omit<SvelteHTMLElements['div'], 'children'>;
let {
class: className,
img,
placeholder,
presence,
ring,
shape = 'circle',
size = 'md',
...props
}: Props = $props();
</script>
<div {...props} class={twMerge('avatar', presence, clsx(className))} class:placeholder>
<div
class={twMerge(
'rounded-xl',
clsx({
'rounded-xl': shape === 'square',
'rounded-full': shape === 'circle',
'w-12': size === 'xs',
'w-16': size === 'sm',
'w-20': size === 'md',
'w-32': size === 'lg',
'avatar-ring ring ring-offset-2 ring-offset-base-100': !!ring,
})
)}
class:bg-neutral={placeholder}
class:ring-neutral={ring === 'neutral'}
class:ring-primary={ring === 'primary'}
class:ring-secondary={ring === 'secondary'}
class:ring-accent={ring === 'accent'}
class:ring-info={ring === 'info'}
class:ring-success={ring === 'success'}
class:ring-warning={ring === 'warning'}
class:ring-error={ring === 'error'}
>
{#if img}
<img src={img} alt={placeholder} />
{:else}
<span
class:text-3xl={size === 'lg'}
class:text-xl={size === 'md'}
class:text-xs={size === 'xs'}>{placeholder}</span
>
{/if}
</div>
</div>

View file

@ -1 +0,0 @@
export { default as Avatar } from './Avatar.svelte';

View file

@ -1,23 +1,19 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import clsx from 'clsx';
import type { Snippet } from 'svelte';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
children?: Snippet;
icon?: Snippet;
status?: Extract<DaisyColor, 'info' | 'success' | 'warning' | 'error'>;
} & SvelteHTMLElements['div'];
};
let { children, class: className, icon, status: color, ...props }: Props = $props();
let { children, icon, status: color }: Props = $props();
</script>
<div
{...props}
role="alert"
class={twMerge('alert', clsx(className))}
class="alert"
class:alert-info={color === 'info'}
class:alert-success={color === 'success'}
class:alert-warning={color === 'warning'}

View file

@ -1,19 +1,16 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
color?: Exclude<DaisyColor, 'ghost'>;
size?: DaisySize | 'md';
variant?: 'spinner' | 'dots' | 'ring' | 'ball' | 'bars' | 'infinity';
} & Pick<SvelteHTMLElements['span'], 'class'>;
let { class: className, color, size = 'md', variant = 'spinner' }: Props = $props();
};
let { color, size = 'md', variant = 'spinner' }: Props = $props();
</script>
<span
class={twMerge('loading', clsx(className))}
class="loading"
class:text-primary={color === 'primary'}
class:text-secondary={color === 'secondary'}
class:text-accent={color === 'accent'}

View file

@ -11,7 +11,6 @@
</script>
<progress
{...props}
class={twMerge('progress', clsx(className))}
class:progress-primary={color === 'primary'}
class:progress-secondary={color === 'secondary'}
@ -20,6 +19,7 @@
class:progress-success={color === 'success'}
class:progress-warning={color === 'warning'}
class:progress-error={color === 'error'}
{...props}
>
{@render children?.()}
</progress>

View file

@ -1,9 +1,12 @@
<script lang="ts">
import clsx from 'clsx';
import type { Snippet } from 'svelte';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = SvelteHTMLElements['div'];
type Props = {
children?: Snippet;
} & SvelteHTMLElements['div'];
let { children, class: className, ...props }: Props = $props();
</script>

View file

@ -14,7 +14,6 @@
</script>
<div
{...props}
class={twMerge('tooltip', clsx(className))}
class:tooltip-primary={color === 'primary'}
class:tooltip-secondary={color === 'secondary'}
@ -29,6 +28,7 @@
class:tooltip-left={position === 'left'}
class:tooltip-right={position === 'right'}
data-tip={tip}
{...props}
>
{@render children?.()}
</div>

View file

@ -13,7 +13,6 @@
</script>
<div
{...props}
class={twMerge('divider', clsx(className))}
class:divider-neutral={color === 'neutral'}
class:divider-primary={color === 'primary'}
@ -27,6 +26,7 @@
class:divider-vertical={direction === 'vertical'}
class:divider-start={variant === 'start'}
class:divider-end={variant === 'end'}
{...props}
>
{@render children?.()}
</div>

View file

@ -10,7 +10,6 @@
</script>
<a
{...props}
class={twMerge('link', clsx(className))}
class:link-primary={color === 'primary'}
class:link-secondary={color === 'secondary'}
@ -20,5 +19,6 @@
class:link-success={color === 'success'}
class:link-warning={color === 'warning'}
class:link-error={color === 'error'}
class:link-hover={hover}>{@render children?.()}</a
class:link-hover={hover}
{...props}>{@render children?.()}</a
>

View file

@ -4,7 +4,7 @@
let { start, center, end }: { start?: Snippet; center?: Snippet; end?: Snippet } = $props();
</script>
<header class="navbar justify-between rounded-box bg-base-200 px-4">
<header class="navbar justify-between bg-base-200 px-4">
<div class="navbar-start">{@render start?.()}</div>
<div class="navbar-center">{@render center?.()}</div>
<div class="navbar-end">{@render end?.()}</div>

View file

@ -1,28 +0,0 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import type { ComponentProps } from 'svelte';
import ResidentTable from './ResidentTable.svelte';
const { Story } = defineMeta({
title: 'Residents/Resident Table',
component: ResidentTable,
});
</script>
{#snippet template(props: ComponentProps<typeof ResidentTable>)}
<ResidentTable {...props} />
{/snippet}
<Story
name="Default"
children={template}
args={{
items: [
{
id: '99fc03e9-3ed1-4047-b9ad-0bf2a9025eaa',
name: 'Resident 1',
phoneNumber: '111-1111',
},
],
}}
/>

View file

@ -1,53 +0,0 @@
<script lang="ts" module>
export type ResidentItem = Pick<Resident, 'id' | 'name' | 'phoneNumber'>;
</script>
<script lang="ts">
import type { Resident } from '@prisma/client';
import clsx from 'clsx';
import { UserRoundPen } from 'lucide-svelte';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
import Button from '../Actions/Button.svelte';
import { messages } from '$lib/i18n';
type Props = {
items?: ResidentItem[];
onEdit?: (resident: ResidentItem) => void;
} & Omit<SvelteHTMLElements['table'], 'children'>;
let { items, onEdit, class: className, ...props }: Props = $props();
</script>
<table {...props} class={twMerge('table', clsx(className))}>
<!-- head -->
<thead>
<tr class="bg-base-100">
<th>#</th>
<th>Name</th>
<th>Phone Number</th>
{#if onEdit}
<th class="flex justify-end"><UserRoundPen class="mx-3" /></th>
{/if}
</tr>
</thead>
<tbody>
<!-- items -->
{#if items}
{#each items as resident, index (resident.id)}
<tr class="hover">
<th>{index + 1}</th>
<td>{resident.name}</td>
<td>{resident.phoneNumber}</td>
{#if onEdit}
<td class="text-end">
<Button size="sm" color="accent" onclick={() => onEdit(resident)}>
{messages.residents_table_edit()}
</Button>
</td>
{/if}
</tr>
{/each}
{/if}
</tbody>
</table>

View file

@ -1,2 +0,0 @@
export { default as ResidentTable } from './ResidentTable.svelte';
export * from './ResidentTable.svelte';

View file

@ -1,64 +0,0 @@
<script lang="ts" module>
export type Recipient = {
id: string;
name: string;
phone: string;
};
</script>
<script lang="ts">
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
recipients: Recipient[];
selected: Recipient[];
} & Omit<SvelteHTMLElements['table'], 'children'>;
let { recipients, selected = $bindable([]), class: className, ...props }: Props = $props();
let checked = $state(recipients.map(() => false));
$effect(() => {
selected = checked
.entries()
.filter(([, val]) => val)
.map(([index]) => recipients[index])
.toArray();
});
</script>
<table {...props} class={twMerge('table', clsx(className))}>
<thead>
<tr>
<th>
<input
class="checkbox"
type="checkbox"
onchange={({ currentTarget }) => {
checked = checked.map(() => currentTarget.checked);
}}
/>
</th>
<th> Name </th>
</tr>
</thead>
<tbody>
{#each recipients as resident, index}
<tr>
<th
><input
class="checkbox"
type="checkbox"
checked={checked[index]}
onchange={({ currentTarget }) => {
checked[index] = currentTarget.checked;
}}
/></th
>
<td>{resident.name}</td>
</tr>
{/each}
</tbody>
</table>

View file

@ -1,2 +0,0 @@
export * from './RecipientList.svelte';
export { default as RecipientList } from './RecipientList.svelte';

View file

@ -0,0 +1,79 @@
import { redirect, type ServerLoadEvent } from '@sveltejs/kit';
import { prisma } from '../prisma';
import { createClerkClient } from '@clerk/backend';
import { CLERK_SECRET_KEY } from '$env/static/private';
import { clerkClient } from 'clerk-sveltekit/server';
import { logger } from '$lib/server/logger';
const clerkSessionClient = createClerkClient({
secretKey: CLERK_SECRET_KEY,
});
export async function validateSession({ locals }: ServerLoadEvent) {
if (!locals.auth.userId || !locals.auth.sessionId) {
return redirect(307, '/login');
}
if ((!locals.auth.orgId && locals.auth.sessionId) || !locals.auth.orgId) {
// Sign out the user if they are not associated with an organization
await clerkSessionClient.sessions.revokeSession(locals.auth.sessionId);
return redirect(307, '/login');
}
const clerkUser = await clerkClient.users.getUser(locals.auth.userId);
const tenantClerkId = locals.auth.orgId;
let tenant = await prisma.tenant.findUnique({
where: {
clerkOrganizationId: tenantClerkId,
},
});
if (!tenant) {
const organization = await clerkClient.organizations.getOrganization({
organizationId: tenantClerkId,
});
tenant = await prisma.tenant.create({
data: {
clerkOrganizationId: tenantClerkId,
name: organization.name,
slug: organization.slug ?? `tenant-${tenantClerkId}`,
},
});
}
let user = await prisma.user.findFirst({
where: {
clerkId: clerkUser.id,
tenantId: tenant.id,
},
});
if (!user) {
if (clerkUser.emailAddresses.length === 0) {
logger.error('User has no email address');
await clerkSessionClient.sessions.revokeSession(locals.auth.sessionId);
return redirect(307, '/login');
}
user = await prisma.user.create({
data: {
clerkId: clerkUser.id,
email: clerkUser.emailAddresses[0].emailAddress,
name: clerkUser.fullName ?? '',
tenantId: tenant.id,
},
});
if (clerkUser.fullName === null) {
logger.error('User has no name');
}
}
return {
user: { name: user.name, hasImage: clerkUser.hasImage, imageUrl: clerkUser.imageUrl },
};
}

View file

@ -1,20 +0,0 @@
import { describe, expect, it, vi } from 'vitest';
import { decrypt, encrypt } from './encryption';
describe('Encryption', () => {
it('should encrypt and decrypt data', () => {
const data = 'aye its ya boi!';
vi.mock('$env/static/private', () => ({
SECRETS_PASSWORD: 'aac7405eb3384e68c285fc252dbf68b2',
SECRETS_SALT: 'c4aeaf8bda72ea45e8c23269ca849013',
SECRETS_IV_POSITION: 9,
}));
const encrypted = encrypt(data);
console.log(encrypted);
const decrypted = decrypt(encrypted);
expect(decrypted).toEqual(data);
});
});

View file

@ -1,39 +0,0 @@
import { SECRETS_PASSWORD, SECRETS_IV_POSITION, SECRETS_SALT } from '$env/static/private';
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
const algorithm = 'aes-256-gcm';
const password = SECRETS_PASSWORD;
const salt = SECRETS_SALT;
const iv_position = Number(SECRETS_IV_POSITION);
function construct(encrypted: string, tag: Buffer, iv: { value: Buffer; position: number }) {
return `${encrypted.slice(0, iv.position)}${iv.value.toString('hex')}${encrypted.slice(iv.position)}${tag.toString('hex')}`;
}
function deconstruct(encrypted: string, position: number): [Buffer, string, Buffer] {
const iv = encrypted.slice(position, position + 16);
const text = `${encrypted.slice(0, position)}${encrypted.slice(position + 16, encrypted.length - 32)}`;
const authTag = encrypted.slice(encrypted.length - 32);
return [Buffer.from(iv, 'hex'), text, Buffer.from(authTag, 'hex')];
}
export function encrypt(value: string): string {
const key = scryptSync(password, salt, 32);
const iv = randomBytes(8);
const cipher = createCipheriv(algorithm, key, iv);
const encrypted = cipher.update(value, 'utf-8', 'hex') + cipher.final('hex');
const authTag = cipher.getAuthTag();
return construct(encrypted, authTag, { value: iv, position: iv_position });
}
export function decrypt(value: string): string {
const key = scryptSync(password, salt, 32);
const [iv, text, authTag] = deconstruct(value, iv_position);
const decipher = createDecipheriv(algorithm, key, iv);
decipher.setAuthTag(authTag);
return decipher.update(text, 'hex', 'utf-8') + decipher.final('utf-8');
}

View file

@ -1 +0,0 @@
export * from './encryption';

View file

@ -1 +0,0 @@
export * from './server';

View file

@ -1 +0,0 @@
export * from './validateSession';

View file

@ -1,105 +0,0 @@
import { logger } from '$lib/server/logger';
import { prisma } from '$lib/server/prisma';
import { type Handle } from '@sveltejs/kit';
import { clerkClient } from 'clerk-sveltekit/server';
const publicRoutes = ['/login'];
const loginRedirect = () =>
new Response(null, {
status: 307,
headers: {
location: '/login',
},
});
async function findOrCreateTenant(tenantClerkId: string) {
const tenant = await prisma.tenant.findUnique({
where: {
clerkOrganizationId: tenantClerkId,
},
});
if (tenant) {
return tenant;
}
const organization = await clerkClient.organizations.getOrganization({
organizationId: tenantClerkId,
});
return await prisma.tenant.create({
data: {
clerkOrganizationId: tenantClerkId,
name: organization.name,
slug: organization.slug ?? `tenant-${tenantClerkId}`,
},
});
}
export function validateSession(): Handle {
return async ({ event: { locals, url, ...rest }, resolve }) => {
// Public route? LET THEM PASS!
if (url !== null && publicRoutes.includes(url.pathname)) {
return resolve({ locals, url, ...rest });
}
// No session, redirect!
if (!locals.auth.sessionId) {
return loginRedirect();
}
// No user, revoke session and redirect!
if (!locals.auth.userId) {
await clerkClient.sessions.revokeSession(locals.auth.sessionId);
return loginRedirect();
}
// No org, signout and redirect!
if (!locals.auth.orgId) {
await clerkClient.sessions.revokeSession(locals.auth.sessionId);
return loginRedirect();
}
// Make sure that a tenant exists for the clerk org
const tenant = await findOrCreateTenant(locals.auth.orgId);
// Make sure a user exists for the clerk user
const clerkUser = await clerkClient.users.getUser(locals.auth.userId);
let user = await prisma.user.findFirst({
where: {
clerkId: clerkUser.id,
tenantId: tenant.id,
},
});
if (!user) {
if (clerkUser.emailAddresses.length === 0) {
logger.error('User has no email address');
await clerkClient.sessions.revokeSession(locals.auth.sessionId);
return loginRedirect();
}
user = await prisma.user.create({
data: {
clerkId: clerkUser.id,
email: clerkUser.emailAddresses[0].emailAddress,
name: clerkUser.fullName ?? '',
tenantId: tenant.id,
},
});
if (clerkUser.fullName === null) {
logger.warn(`User {${user.id}} has no name!`);
}
}
// Load user and tenant into locals
locals.user = user;
locals.tenant = tenant;
return resolve({ locals, url, ...rest });
};
}

View file

@ -1,17 +1,3 @@
import { PrismaClient } from '@prisma/client';
import { logger } from '../logger';
export const prisma = new PrismaClient({
log: [
{ emit: 'event', level: 'query' },
{ emit: 'event', level: 'info' },
],
});
prisma.$on('query', (event) => {
logger.debug(`Query [${event.duration}ms]: ${event.query}`);
});
prisma.$on('info', (event) => {
logger.info(event.message);
});
export const prisma = new PrismaClient();

View file

@ -0,0 +1,4 @@
import { TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN } from '$env/static/private';
import twilio from 'twilio';
export const TwilioClient = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

View file

@ -1,20 +1 @@
import type { TwilioConfig } from '@prisma/client';
import { decrypt, encrypt } from '../crypto';
type TwilioCore = Pick<TwilioConfig, 'accountSID' | 'authToken' | 'phoneNumber'>;
export function encryptTwilioConfig({ accountSID, authToken, phoneNumber }: TwilioCore) {
return {
accountSID: encrypt(accountSID),
authToken: encrypt(authToken),
phoneNumber: encrypt(phoneNumber),
};
}
export function decryptTwilioConfig({ accountSID, authToken, phoneNumber }: TwilioCore) {
return {
accountSID: decrypt(accountSID),
authToken: decrypt(authToken),
phoneNumber: decrypt(phoneNumber),
};
}
export * from './client';

View file

@ -8,16 +8,16 @@
let { children } = $props();
</script>
<ParaglideJS {i18n}>
<ClerkProvider publishableKey={PUBLIC_CLERK_PUBLISHABLE_KEY}>
<div class="layout">
<div class="layout">
<ParaglideJS {i18n}>
<ClerkProvider publishableKey={PUBLIC_CLERK_PUBLISHABLE_KEY}>
{@render children()}
</div>
</ClerkProvider>
</ParaglideJS>
</ClerkProvider>
</ParaglideJS>
</div>
<style>
.layout {
@apply h-screen w-screen p-8;
@apply h-screen w-screen bg-base-100;
}
</style>

View file

@ -0,0 +1,3 @@
import { validateSession } from '$lib/server/auth';
export const load = async (event) => validateSession(event);

View file

@ -1,13 +1,3 @@
import { clerkClient } from 'clerk-sveltekit/server';
import { validateSession } from '$lib/server/auth';
export const load = async ({ locals }) => {
const clerkUser = await clerkClient.users.getUser(locals.auth.userId!);
return {
user: {
name: clerkUser.fullName || '',
hasImage: clerkUser.hasImage,
imageUrl: clerkUser.imageUrl,
},
};
};
export const load = async (event) => validateSession(event);

View file

@ -5,15 +5,17 @@
import { messages } from '$lib/i18n';
import 'clerk-sveltekit/client';
import SignOutButton from 'clerk-sveltekit/client/SignOutButton.svelte';
import { Cog, LogOut, Menu, MessageCircleMore, UsersRound } from 'lucide-svelte';
import { LogOut, MessageCircleMore } from 'lucide-svelte';
import type { Snippet } from 'svelte';
import { onMount } from 'svelte';
import type { PageData } from './$types';
type Props = {
children: Snippet;
data: PageData;
};
let { children }: Props = $props();
let { children, data }: Props = $props();
let clerk;
@ -43,68 +45,52 @@
}
}
});
let message = $derived(messages.nav_greeting({ name: data.user.name }));
</script>
{#snippet userMenu()}
<div class="dropdown dropdown-end">
<div
tabindex={0}
role="button"
class="btn btn-primary btn-md flex items-center gap-2 text-lg"
>
<Menu />
Menu
<div tabindex="0" role="button" class="btn btn-circle btn-primary btn-sm ring">
<div class="avatar placeholder online">
<div class="w-8 rounded-full">
{#if data.user.hasImage}
<img src={data.user.imageUrl} alt="Avatar" />
{:else}
<span>{data.user.name.at(0)}</span>
{/if}
</div>
</div>
</div>
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<ul
tabindex="0"
class="menu dropdown-content menu-lg z-[1] mt-4 w-52 rounded-box border border-neutral bg-base-200 p-2 text-right shadow"
class="menu dropdown-content menu-lg z-[1] mt-4 w-52 rounded-box bg-base-200 p-2 text-right shadow"
>
<li>
<button onclick={() => goto('/app/sms')}>
<MessageCircleMore />
{messages.nav_menu_sms()}
</button>
</li>
<li>
<button onclick={() => goto('/app/residents')}>
<UsersRound />
{messages.nav_menu_residents()}
</button>
</li>
<li>
<button onclick={() => goto('/app/settings')}>
<Cog />
{messages.nav_menu_settings()}
</button>
</li>
<li>
<SignOutButton>
<LogOut />
{messages.nav_menu_logout()}
</SignOutButton>
<button onclick={() => goto('/app/sms')}
><MessageCircleMore /> {messages.nav_menu_sms()}</button
>
</li>
<li><SignOutButton><LogOut /> {messages.nav_menu_logout()}</SignOutButton></li>
</ul>
</div>
{/snippet}
<div class="flex h-full flex-col items-stretch gap-4">
<Navbar>
{#snippet start()}
<Button onclick={() => goto('/app')} class="rounded-box" color="ghost">
<h2 class="prose prose-xl">Hestia</h2>
</Button>
{/snippet}
{#snippet center()}
<h1 class="prose prose-2xl">Svelte</h1>
{/snippet}
{#snippet end()}
<div class="flex items-center gap-4 pr-2">
{@render userMenu()}
</div>
{/snippet}
</Navbar>
<div class="h-full rounded-box bg-base-200 p-8">
{@render children()}
</div>
</div>
<Navbar>
{#snippet start()}
<Button onclick={() => goto('/app')}>
<h2 class="prose prose-xl">Hestia</h2>
</Button>
{/snippet}
{#snippet center()}
<h1 class="prose prose-2xl">Svelte</h1>
{/snippet}
{#snippet end()}
<div class="flex items-center gap-3">
<p class="prose prose-lg">{message}</p>
{@render userMenu()}
</div>
{/snippet}
</Navbar>
{@render children()}

View file

@ -1,19 +1,2 @@
<script lang="ts">
import { Avatar } from '$lib/components/Datadisplay';
import { messages } from '$lib/i18n';
import type { PageData } from './$types';
type Props = {
data: PageData;
};
let { data }: Props = $props();
let message = $derived(messages.nav_greeting({ name: data.user.name }));
let initials = $derived(data.user.name.split(' ').reduce((col, name) => col + name.at(0), ''));
</script>
<div class="flex flex-col items-center gap-2">
<Avatar img={data.user.imageUrl} placeholder={initials} size="sm" ring="primary" />
<h1 class="prose prose-2xl">{message}</h1>
</div>

View file

@ -1,90 +0,0 @@
import { PhoneRegex } from '$lib/regex/phone.js';
import { logger } from '$lib/server/logger/index.js';
import { prisma } from '$lib/server/prisma';
import { fail } from '@sveltejs/kit';
import zod from 'zod';
export const load = async ({ locals }) => {
const residents = await prisma.resident.findMany({
where: {
tenantId: locals.tenant.id,
},
select: {
id: true,
name: true,
phoneNumber: true,
},
});
return {
residents: residents,
};
};
export const actions = {
upsert: async (event) => {
const form = await event.request.formData();
if (!form.has('name')) {
return fail(400, { error: 'phone_missing' });
}
if (!form.get('phoneNumber')) {
return fail(400, { error: 'message_missing' });
}
const id = form.get('id');
if (id && typeof id !== 'string') {
return fail(400, { error: 'invalid_id' });
}
const name = form.get('name');
if (typeof name !== 'string') {
return fail(400, { error: 'invalid_name' });
}
const {
success: phoneSuccess,
data: phone,
error: phoneError,
} = zod.string().regex(PhoneRegex).safeParse(form.get('phoneNumber'));
if (!phoneSuccess) {
logger.error(phoneError);
return fail(400, { error: 'invalid_phone' });
}
await prisma.resident.upsert({
where: {
id: id ?? '',
},
create: {
name: name,
phoneNumber: phone,
tenantId: event.locals.tenant.id,
},
update: {
name: name,
phoneNumber: phone,
},
});
},
delete: async (event) => {
const form = await event.request.formData();
logger.info('Deleting Resident');
if (!form.has('id')) {
return fail(400, { error: 'id_missing' });
}
const id = form.get('id');
if (typeof id !== 'string') {
return fail(400, { error: 'invalid_id' });
}
await prisma.resident.delete({
where: {
id: id,
},
});
},
};

View file

@ -1,98 +0,0 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { Button, Modal, ModalActions, ModalBody } from '$lib/components/Actions';
import { TextInput } from '$lib/components/DataInput';
import { ResidentTable, type ResidentItem } from '$lib/components/Residents';
import { messages } from '$lib/i18n';
import { Phone, UserRound, UserRoundPlus, X } from 'lucide-svelte';
import type { ActionData, PageData } from './$types';
type Props = {
data: PageData;
form: ActionData;
};
let { data }: Props = $props();
let residents = $derived(data.residents);
let dialog: HTMLDialogElement | undefined = $state(undefined);
let form: HTMLFormElement | undefined = $state(undefined);
let resident: ResidentItem | undefined = $state(undefined);
</script>
<Modal
backdrop
bind:dialog
onclose={() => {
resident = undefined;
form?.reset();
}}
>
<ModalBody>
<div class="flex items-center justify-between">
<h2 class="text-2xl">
{resident
? messages.residents_modal_title_edit()
: messages.residents_modal_title_new()}
</h2>
<form method="dialog">
<Button color="ghost" shape="square" size="sm">
<X />
</Button>
</form>
</div>
<form method="POST" action="?/upsert" bind:this={form} use:enhance>
{#if resident}
<input type="hidden" name="id" value={resident.id} />
{/if}
<TextInput bordered name="name" value={resident?.name}>
{#snippet label()}
<UserRound size="18" />
{messages.residents_modal_label_name()}
{/snippet}
</TextInput>
<TextInput bordered name="phoneNumber" type={'tel'} value={resident?.phoneNumber}>
{#snippet label()}
<Phone size="18" />
{messages.residents_modal_label_phone()}
{/snippet}
</TextInput>
<ModalActions class="flex">
<Button
class="grow"
type="submit"
formaction="?/delete"
color="error"
onclick={() => dialog?.close()}
>
{messages.residents_modal_delete()}
</Button>
<Button class="grow" type="submit" color="primary" onclick={() => dialog?.close()}>
{messages.residents_modal_submit()}
</Button>
</ModalActions>
</form>
</ModalBody>
</Modal>
<div class="mx-auto flex max-w-5xl flex-col items-stretch gap-2">
<div class="flex items-center justify-between">
<h1 class="text-4xl">{messages.residents_title()}</h1>
<Button
onclick={() => {
dialog?.showModal();
}}
color="primary"><UserRoundPlus size="18" />{messages.residents_button_new()}</Button
>
</div>
<div class="divider"></div>
<ResidentTable
items={residents}
onEdit={(r) => {
resident = r;
dialog?.showModal();
}}
/>
</div>

View file

@ -1,105 +0,0 @@
import { PhoneRegex } from '$lib/regex';
import { logger } from '$lib/server/logger';
import { prisma } from '$lib/server/prisma';
import { encryptTwilioConfig, decryptTwilioConfig } from '$lib/server/twilio';
import { fail, type Actions } from '@sveltejs/kit';
import zod from 'zod';
export const load = async (event) => {
const tenantId = event.locals.tenant.id;
const configs = await prisma.tenantConfig.findUnique({
where: { tenantId: tenantId },
select: {
twilioConfig: {
select: {
accountSID: true,
authToken: true,
phoneNumber: true,
},
},
},
});
if (!configs) {
return {};
}
return {
configs: {
...(configs.twilioConfig && {
twilioConfig: decryptTwilioConfig(configs.twilioConfig),
}),
},
};
};
export const actions = {
default: async (event) => {
const form = await event.request.formData();
const tenantId = event.locals.tenant.id;
if (!form.has('twilioAccountSID')) {
return fail(400, { error: 'account_sid_missing' });
}
if (!form.has('twilioAuthToken')) {
return fail(400, { error: 'auth_token_missing' });
}
if (!form.has('twilioPhoneNumber')) {
return fail(400, { error: 'phone_number_missing' });
}
const accountSID = form.get('twilioAccountSID');
if (typeof accountSID !== 'string') {
return fail(400, { error: 'invalid_account_sid' });
}
const authToken = form.get('twilioAuthToken');
if (typeof authToken !== 'string') {
return fail(400, { error: 'invalid_auth_token' });
}
const {
success: phoneSuccess,
data: phoneNumber,
error: phoneError,
} = zod.string().regex(PhoneRegex).safeParse(form.get('twilioPhoneNumber'));
if (!phoneSuccess) {
logger.error(phoneError);
return fail(400, { error: 'invalid_phone_number' });
}
const configs = await prisma.tenantConfig.upsert({
where: {
tenantId: tenantId,
},
create: {
tenantId: tenantId,
twilioConfig: {
create: encryptTwilioConfig({
accountSID: accountSID,
authToken: authToken,
phoneNumber: phoneNumber,
}),
},
},
update: {
tenantId: tenantId,
twilioConfig: {
update: encryptTwilioConfig({
accountSID: accountSID,
authToken: authToken,
phoneNumber: phoneNumber,
}),
},
},
select: { twilioConfig: true },
});
return {
configs: {
...(configs.twilioConfig && {
twilioConfig: decryptTwilioConfig(configs.twilioConfig),
}),
},
};
},
} satisfies Actions;

View file

@ -1,90 +0,0 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { Button } from '$lib/components/Actions';
import { TextInput } from '$lib/components/DataInput';
import Divider from '$lib/components/Layout/Divider.svelte';
import { messages } from '$lib/i18n';
import { Fingerprint, KeyRound, PhoneOutgoing } from 'lucide-svelte';
import type { ActionData, PageData } from './$types';
type Props = {
data: PageData;
form: ActionData;
};
let { data, form }: Props = $props();
let configs = $derived(form?.configs ?? data.configs);
</script>
<div class="page">
<div class="card w-full max-w-xl bg-base-200 px-4 pt-4 shadow-xl">
<div class="card-title justify-center">
<h2 class="text-2xl font-semibold">{messages.settings_title()}</h2>
{#if form?.error}
<div role="alert" class="alert alert-error absolute -top-20">
<i class="fi fi-bs-octagon-xmark h-6 w-6 shrink-0"></i>
<span>{form.error}</span>
</div>
{/if}
</div>
<form id="sms" method="POST" use:enhance>
<div class="card-body">
<Divider />
<!-- Twilio -->
<h2 class="text-2xl font-semibold">{messages.settings_category_twilio()}</h2>
<TextInput
defaultvalue={configs?.twilioConfig?.accountSID}
name="twilioAccountSID"
placeholder="..."
bordered
fade
>
{#snippet label()}
<div class="flex gap-2">
<Fingerprint size="18" />
{messages.settings_twilio_account_sid()}
</div>
{/snippet}
</TextInput>
<TextInput
defaultvalue={configs?.twilioConfig?.authToken}
name="twilioAuthToken"
placeholder="..."
type="password"
bordered
fade
>
{#snippet label()}
<div class="flex gap-2">
<KeyRound size="18" />
{messages.settings_twilio_auth_token()}
</div>
{/snippet}
</TextInput>
<TextInput
defaultvalue={configs?.twilioConfig?.phoneNumber}
name="twilioPhoneNumber"
placeholder="+1XXX-XXX-XXXX"
bordered
fade
>
{#snippet label()}
<div class="flex gap-2">
<PhoneOutgoing size="18" />
{messages.settings_twilio_phone_number()}
</div>
{/snippet}
</TextInput>
</div>
<div class="card-actions justify-center px-8 pb-4">
<Button type="submit" variant="outline" full>{messages.settings_save()}</Button>
</div>
</form>
</div>
</div>
<style>
.page {
@apply flex flex-col items-center justify-around gap-24 py-[10%];
}
</style>

View file

@ -1,110 +1,47 @@
import type { Recipient } from '$lib/components/SMS';
import { TWILIO_PHONE_NUMBER } from '$env/static/private';
import { PhoneRegex } from '$lib/regex';
import { logger } from '$lib/server/logger';
import { prisma } from '$lib/server/prisma/index.js';
import { decryptTwilioConfig } from '$lib/server/twilio/index.js';
import { TwilioClient } from '$lib/server/twilio';
import { fail, type Actions } from '@sveltejs/kit';
import twilio from 'twilio';
import zod from 'zod';
export const load = async (event) => {
const tenantId = event.locals.tenant.id;
const configs = await prisma.tenantConfig.findUnique({
where: { tenantId: tenantId },
select: {
twilioConfig: {
select: {
accountSID: true,
authToken: true,
phoneNumber: true,
},
},
},
});
const { success, error: validationError } = zod
.object({
accountSID: zod.string(),
authToken: zod.string(),
phoneNumber: zod.string(),
})
.safeParse(configs?.twilioConfig);
if (!success) {
logger.warn(validationError.message);
}
const residents = await prisma.resident.findMany({
where: {
tenantId: event.locals.tenant.id,
},
select: {
id: true,
name: true,
phoneNumber: true,
},
});
return {
isTwilioConfigured: success,
residents: residents,
};
};
export const actions = {
default: async (event) => {
push: async (event) => {
const form = await event.request.formData();
if (!form.has('recipients')) {
return fail(400, { error: 'recipients_missing' });
if (!form.has('phone')) {
return fail(400, { error: 'phone_missing' });
}
if (!form.has('message')) {
if (!form.get('message')) {
return fail(400, { error: 'message_missing' });
}
const recipients: Recipient[] = JSON.parse(form.get('recipients') as string);
if (!Array.isArray(recipients)) {
return fail(400, { error: 'invalid_recipients' });
const {
success: phoneSuccess,
data: phone,
error: phoneError,
} = zod.string().regex(PhoneRegex).safeParse(form.get('phone'));
if (!phoneSuccess) {
logger.error(phoneError);
return fail(400, { error: 'invalid_phone' });
}
logger.info(recipients);
const message = form.get('message');
if (typeof message !== 'string') {
return fail(400, { error: 'invalid_message' });
}
const tenant = await prisma.tenantConfig.findUnique({
where: {
tenantId: event.locals.tenant.id,
},
select: {
twilioConfig: true,
},
});
const config = tenant?.twilioConfig;
if (!config) {
return fail(307, { error: 'no_twilio_config' });
try {
const result = await TwilioClient.messages.create({
to: phone,
body: message,
from: TWILIO_PHONE_NUMBER,
});
logger.debug(result);
} catch (e) {
logger.error(e);
fail(500, { success: false });
}
const decryptedConfig = decryptTwilioConfig(config);
const client = twilio(decryptedConfig.accountSID, decryptedConfig.authToken);
for (const recipient of recipients) {
try {
const result = await client.messages.create({
to: recipient.phone,
body: message,
from: decryptedConfig.phoneNumber,
});
logger.debug(result);
} catch (e) {
logger.error(e);
fail(500, { success: false });
}
}
return {
success: true,
};

View file

@ -1,80 +1,68 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { goto } from '$app/navigation';
import { Button } from '$lib/components/Actions';
import { Textarea } from '$lib/components/DataInput';
import { Alert } from '$lib/components/Feedback';
import { Link } from '$lib/components/Navigation';
import { RecipientList, type Recipient } from '$lib/components/SMS';
import { Textarea, TextInput } from '$lib/components/DataInput';
import { fade } from 'svelte/transition';
import type { ActionData } from './$types';
import { messages } from '$lib/i18n';
import { CircleX, MessageCircleMore, TriangleAlert } from 'lucide-svelte';
import type { ActionData, PageData } from './$types';
type Props = {
data: PageData;
form: ActionData;
};
let { data, form }: Props = $props();
let recipients = $derived(
data.residents.map((r) => ({ id: r.id, name: r.name, phone: r.phoneNumber }))
);
let selectedRecipients: Recipient[] = $state([]);
let { form }: Props = $props();
</script>
<div class="flex flex-col items-center gap-4">
{#if form?.error}
<Alert class="flex w-fit justify-center" status="error">
{#snippet icon()}
<CircleX />
{/snippet}
<span>{form.error}</span>
</Alert>
{:else if !data.isTwilioConfigured}
<Alert class="flex w-fit justify-center" status="warning">
{#snippet icon()}
<TriangleAlert />
{/snippet}
<span>
Twilio must be configured on the <Link onclick={() => goto('/app/settings')}
>Settings</Link
> page
</span>
</Alert>
{/if}
<h2 class="text-4xl font-semibold">{messages.sms_prompt()}</h2>
<div class="flex justify-center gap-4">
<div class="flex flex-col items-center gap-2">
<h2 class="text-3xl font-medium">Recipients</h2>
<div class="h-full overflow-y-scroll rounded-lg bg-base-100 p-4">
<RecipientList {recipients} bind:selected={selectedRecipients} />
</div>
{#snippet PhoneLabel()}
<i class="fi fi-sr-phone-flip"></i> {messages.sms_label_phone()}
{/snippet}
{#snippet MessageLabel()}
<i class="fi fi-sr-comment-alt"></i> {messages.sms_label_message()}
{/snippet}
{#snippet alert(message: string)}
<div role="alert" class="alert alert-error absolute -top-20" transition:fade>
<i class="fi fi-bs-octagon-xmark h-6 w-6 shrink-0"></i>
<span>{message}</span>
</div>
{/snippet}
<div class="page" transition:fade>
<div class="card bg-base-200 px-4 pt-4 shadow-xl">
<div class="card-title justify-center">
<h2 class="text-2xl font-semibold">{messages.sms_prompt()}</h2>
{#if form?.error}
{@render alert(form.error)}
{/if}
</div>
<form id="sms" method="POST" use:enhance>
<div class="flex flex-col gap-4">
<input name="recipients" type="hidden" value={JSON.stringify(selectedRecipients)} />
<form id="sms" method="POST" action="?/push" use:enhance>
<div class="card-body">
<TextInput
type="tel"
name="phone"
label={PhoneLabel}
placeholder="XXX-XXX-XXXX"
bordered
fade
/>
<Textarea
disabled={!data.isTwilioConfigured}
label={MessageLabel}
size="lg"
error={form?.error}
name="message"
placeholder="..."
form="sms"
resizable={false}
cols={40}
rows={12}
>
{#snippet label()}
<span class="flex items-center gap-2 text-xl">
<MessageCircleMore size="22" />
{messages.sms_label_message()}
</span>
{/snippet}
</Textarea>
<Button full color="primary" disabled={!data.isTwilioConfigured} type="submit">
{messages.sms_button_submit()}
</Button>
/>
</div>
<div class="card-actions justify-center px-8 pb-4">
<Button type="submit" variant="outline" full>{messages.sms_button_submit()}</Button>
</div>
</form>
</div>
</div>
<style>
.page {
@apply flex flex-col items-center justify-around gap-24 py-[10%];
}
</style>

View file

@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-node';
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */