WIP rahvatarkus, working CRUD demo

This commit is contained in:
Mihkel Martin Kasterpalu 2025-02-11 14:46:14 +02:00
parent 11ade8fc06
commit 1674cdd726
14 changed files with 1015 additions and 15 deletions

View file

@ -27,34 +27,38 @@
"@types/better-sqlite3": "^7.6.12",
"@types/spotify-web-api-node": "^5.0.11",
"autoprefixer": "^10.4.20",
"bits-ui": "1.0.0-next.86",
"bits-ui": "1.0.0-next.94",
"clsx": "^2.1.1",
"drizzle-kit": "^0.30.2",
"eslint": "^9.19.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^2.46.1",
"formsnap": "^2.0.0",
"globals": "^15.14.0",
"lucide-svelte": "^0.474.0",
"mode-watcher": "^0.5.1",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.10",
"svelte": "^5.19.1",
"svelte-check": "^4.1.4",
"svelte-dnd-action": "^0.9.55",
"svelte-sonner": "^0.3.28",
"sveltekit-superforms": "^2.23.1",
"tailwind-merge": "^2.6.0",
"tailwind-variants": "^0.3.1",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.7.3",
"typescript-eslint": "^8.22.0",
"vite": "^6.0.11"
"vite": "^6.0.11",
"zod": "^3.24.1"
},
"dependencies": {
"@fontsource-variable/kode-mono": "^5.1.1",
"@fontsource-variable/smooch-sans": "^5.1.1",
"better-sqlite3": "^11.8.0",
"drizzle-orm": "^0.38.4",
"mode-watcher": "^0.5.1",
"nanoid": "^5.0.9",
"spotify-web-api-node": "^5.0.2",
"svelte-kit-sessions": "^0.4.0"

465
pnpm-lock.yaml generated
View file

@ -20,9 +20,6 @@ importers:
drizzle-orm:
specifier: ^0.38.4
version: 0.38.4(@types/better-sqlite3@7.6.12)(better-sqlite3@11.8.1)
mode-watcher:
specifier: ^0.5.1
version: 0.5.1(svelte@5.19.1)
nanoid:
specifier: ^5.0.9
version: 5.0.9
@ -67,8 +64,8 @@ importers:
specifier: ^10.4.20
version: 10.4.20(postcss@8.5.1)
bits-ui:
specifier: 1.0.0-next.86
version: 1.0.0-next.86(svelte@5.19.1)
specifier: 1.0.0-next.94
version: 1.0.0-next.94(svelte@5.19.1)
clsx:
specifier: ^2.1.1
version: 2.1.1
@ -84,12 +81,18 @@ importers:
eslint-plugin-svelte:
specifier: ^2.46.1
version: 2.46.1(eslint@9.19.0(jiti@1.21.7))(svelte@5.19.1)
formsnap:
specifier: ^2.0.0
version: 2.0.0(svelte@5.19.1)(sveltekit-superforms@2.23.1(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(@types/json-schema@7.0.15)(svelte@5.19.1)(typescript@5.7.3))
globals:
specifier: ^15.14.0
version: 15.14.0
lucide-svelte:
specifier: ^0.474.0
version: 0.474.0(svelte@5.19.1)
mode-watcher:
specifier: ^0.5.1
version: 0.5.1(svelte@5.19.1)
prettier:
specifier: ^3.4.2
version: 3.4.2
@ -108,6 +111,12 @@ importers:
svelte-dnd-action:
specifier: ^0.9.55
version: 0.9.55(svelte@5.19.1)
svelte-sonner:
specifier: ^0.3.28
version: 0.3.28(svelte@5.19.1)
sveltekit-superforms:
specifier: ^2.23.1
version: 2.23.1(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(@types/json-schema@7.0.15)(svelte@5.19.1)(typescript@5.7.3)
tailwind-merge:
specifier: ^2.6.0
version: 2.6.0
@ -129,6 +138,9 @@ importers:
vite:
specifier: ^6.0.11
version: 6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)
zod:
specifier: ^3.24.1
version: 3.24.1
packages:
@ -140,6 +152,16 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@ark/schema@0.39.0':
resolution: {integrity: sha512-LQbQUb3Sj461LgklXObAyUJNtsUUCBxZlO2HqRLYvRSqpStm0xTMrXn51DwBNNxeSULvKVpXFwoxiSec9kwKww==}
'@ark/util@0.39.0':
resolution: {integrity: sha512-90APHVklk8BP4kku7hIh1BgrhuyKYqoZ4O7EybtFRo7cDl9mIyc/QUbGvYDg//73s0J2H0I/gW9pzroA1R4IBQ==}
'@babel/runtime@7.26.7':
resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==}
engines: {node: '>=6.9.0'}
'@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
@ -617,6 +639,9 @@ packages:
resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@exodus/schemasafe@1.3.0':
resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==}
'@floating-ui/core@1.6.9':
resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==}
@ -632,6 +657,16 @@ packages:
'@fontsource-variable/smooch-sans@5.1.1':
resolution: {integrity: sha512-ADcY3Pjkvu74x4T+p2gNIo8Ba7DGrX1IhQgJFefztWB8Tf5nX7Qb+tqAbtW7O/KYWJi4EjzrxCefHBM1PfOOsg==}
'@gcornut/valibot-json-schema@0.31.0':
resolution: {integrity: sha512-3xGptCurm23e7nuPQkdrE5rEs1FeTPHhAUsBuwwqG4/YeZLwJOoYZv+fmsppUEfo5y9lzUwNQrNqLS/q7HMc7g==}
hasBin: true
'@hapi/hoek@9.3.0':
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
'@hapi/topo@5.1.0':
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
'@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'}
@ -805,6 +840,10 @@ packages:
'@polka/url@1.0.0-next.28':
resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
'@poppinss/macroable@1.0.4':
resolution: {integrity: sha512-ct43jurbe7lsUX5eIrj4ijO3j/6zIPp7CDnFWXDs7UPAbw1Pu1iH3oAmFdP4jcskKJBURH5M9oTtyeiUXyHX8Q==}
engines: {node: '>=18.16.0'}
'@rollup/plugin-commonjs@28.0.2':
resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==}
engines: {node: '>=16.0.0 || 14 >= 14.17'}
@ -936,6 +975,18 @@ packages:
cpu: [x64]
os: [win32]
'@sideway/address@4.1.5':
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
'@sideway/formula@3.0.1':
resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
'@sideway/pinpoint@2.0.0':
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
'@sinclair/typebox@0.34.16':
resolution: {integrity: sha512-rIljj8VPYAfn26ANY+5pCNVBPiv6hSufuKGe46y65cJZpvx8vHvPXlU0Q/Le4OGtlNaL8Jg2FuhtvQX18lSIqA==}
'@sveltejs/adapter-node@5.2.12':
resolution: {integrity: sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==}
peerDependencies:
@ -1008,6 +1059,25 @@ packages:
'@types/spotify-web-api-node@5.0.11':
resolution: {integrity: sha512-RS3IkSqH9geC61e8qd+Oy7giOTtiY7ywm0Z4bu5uYuc7XuOcLfDwKjmle85IbpTEdazeCgmIbo8nMLg7WDVvgw==}
'@types/validator@13.12.2':
resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==}
'@typeschema/class-validator@0.3.0':
resolution: {integrity: sha512-OJSFeZDIQ8EK1HTljKLT5CItM2wsbgczLN8tMEfz3I1Lmhc5TBfkZ0eikFzUC16tI3d1Nag7um6TfCgp2I2Bww==}
peerDependencies:
class-validator: ^0.14.1
peerDependenciesMeta:
class-validator:
optional: true
'@typeschema/core@0.14.0':
resolution: {integrity: sha512-Ia6PtZHcL3KqsAWXjMi5xIyZ7XMH4aSnOQes8mfMLx+wGFGtGRNlwe6Y7cYvX+WfNK67OL0/HSe9t8QDygV0/w==}
peerDependencies:
'@types/json-schema': ^7.0.15
peerDependenciesMeta:
'@types/json-schema':
optional: true
'@typescript-eslint/eslint-plugin@8.22.0':
resolution: {integrity: sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -1055,6 +1125,14 @@ packages:
resolution: {integrity: sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@vinejs/compiler@3.0.0':
resolution: {integrity: sha512-v9Lsv59nR56+bmy2p0+czjZxsLHwaibJ+SV5iK9JJfehlJMa501jUJQqqz4X/OqKXrxtE3uTQmSqjUqzF3B2mw==}
engines: {node: '>=18.0.0'}
'@vinejs/vine@3.0.0':
resolution: {integrity: sha512-GeCAHLzKkL2kMFqatgqyiiNh+FILOSAV8x8imBDo6AWQ91w30Kaxw4FnzUDqgcd9z8aCYOBQ7RJxBBGfyr+USQ==}
engines: {node: '>=18.16.0'}
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@ -1106,6 +1184,9 @@ packages:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
arktype@2.0.4:
resolution: {integrity: sha512-S68rWVDnJauwH7/QCm8zCUM3aTe9Xk6oRihdcc3FSUAtxCo/q1Fwq46JhcwB5Ufv1YStwdQRz+00Y/URlvbhAQ==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
@ -1136,8 +1217,8 @@ packages:
bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
bits-ui@1.0.0-next.86:
resolution: {integrity: sha512-C2sTO3sasGoRhoMG2CUUsGfOhAoRL5Jc4pVB6AxoKQ+FBmX/uG9K1tW44eT/801iMoH+QeaH6fNCnoshpZtS8A==}
bits-ui@1.0.0-next.94:
resolution: {integrity: sha512-aGr6iMg16olEVFDYOUDfMB/uvExdmFuNLeILxv3MkwD3plTIbBBanA5/KLVfEnzT0uFgoEIrDV/unLfLG/BmEQ==}
engines: {node: '>=18', pnpm: '>=8.7.0'}
peerDependencies:
svelte: ^5.11.0
@ -1182,6 +1263,10 @@ packages:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
camelcase@8.0.0:
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
engines: {node: '>=16'}
caniuse-lite@1.0.30001695:
resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==}
@ -1200,6 +1285,9 @@ packages:
chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
class-validator@0.14.1:
resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==}
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
@ -1251,6 +1339,9 @@ packages:
engines: {node: '>=4'}
hasBin: true
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
@ -1395,6 +1486,9 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
effect@3.12.10:
resolution: {integrity: sha512-fGg3sEN+l1rffWJvXICBTqyyzpa4y1uNo3aI/JLezJDmDk0Qj/WHiy6wVe0cecdr0eFU0XkZcFu2TWpgV8kBiw==}
electron-to-chromium@1.5.84:
resolution: {integrity: sha512-I+DQ8xgafao9Ha6y0qjHHvpZ9OfyA1qKlkHkjywxzniORU2awxyz7f/iVJcULmrF2yrM3nHQf+iDjJtbbexd/g==}
@ -1424,6 +1518,12 @@ packages:
peerDependencies:
esbuild: '>=0.12 <1'
esbuild-runner@2.2.2:
resolution: {integrity: sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==}
hasBin: true
peerDependencies:
esbuild: '*'
esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}
@ -1532,6 +1632,10 @@ packages:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
fast-check@3.23.2:
resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
engines: {node: '>=8.0.0'}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@ -1593,6 +1697,13 @@ packages:
resolution: {integrity: sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==}
deprecated: 'Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau'
formsnap@2.0.0:
resolution: {integrity: sha512-W61elddvdzeBEs10nNvwxQnx/FctJFHBXPk9uluNQAckHo1nuSUvSQGIjtLjTKIbQdQnwEOoxqWrk9tuv0U7hA==}
engines: {node: '>=18', pnpm: '>=8.7.0'}
peerDependencies:
svelte: ^5.0.0
sveltekit-superforms: ^2.19.0
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@ -1737,6 +1848,9 @@ packages:
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
hasBin: true
joi@17.13.3:
resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
@ -1744,6 +1858,10 @@ packages:
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
json-schema-to-ts@3.1.1:
resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==}
engines: {node: '>=16'}
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@ -1764,6 +1882,9 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
libphonenumber-js@1.11.19:
resolution: {integrity: sha512-bW/Yp/9dod6fmyR+XqSUL1N5JE7QRxQ3KrBIbYS1FTv32e5i3SEtQVX+71CYNv8maWNSOgnlCoNp9X78f/cKiA==}
lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'}
@ -1800,6 +1921,9 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
memoize-weak@1.0.2:
resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==}
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@ -1900,6 +2024,10 @@ packages:
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
engines: {node: '>=0.10.0'}
normalize-url@8.0.1:
resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
engines: {node: '>=14.16'}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@ -2108,6 +2236,9 @@ packages:
engines: {node: '>=14'}
hasBin: true
property-expr@2.0.6:
resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==}
pump@3.0.2:
resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==}
@ -2115,6 +2246,9 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
pure-rand@6.1.0:
resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
@ -2141,6 +2275,9 @@ packages:
resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==}
engines: {node: '>= 14.18.0'}
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@ -2284,6 +2421,10 @@ packages:
engines: {node: '>= 7.0.0'}
deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net
superstruct@2.0.2:
resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==}
engines: {node: '>=14.0.0'}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@ -2325,6 +2466,17 @@ packages:
peerDependencies:
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
svelte-sonner@0.3.28:
resolution: {integrity: sha512-K3AmlySeFifF/cKgsYNv5uXqMVNln0NBAacOYgmkQStLa/UoU0LhfAACU6Gr+YYC8bOCHdVmFNoKuDbMEsppJg==}
peerDependencies:
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
svelte-toolbelt@0.5.0:
resolution: {integrity: sha512-t3tenZcnfQoIeRuQf/jBU7bvTeT3TGkcEE+1EUr5orp0lR7NEpprflpuie3x9Dn0W9nOKqs3HwKGJeeN5Ok1sQ==}
engines: {node: '>=18', pnpm: '>=8.7.0'}
peerDependencies:
svelte: ^5.0.0-next.126
svelte-toolbelt@0.7.1:
resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==}
engines: {node: '>=18', pnpm: '>=8.7.0'}
@ -2335,6 +2487,15 @@ packages:
resolution: {integrity: sha512-H/Vs2O51bwILZbaNUSdr4P1NbLpOGsxl4jJAjd88ELjzRgeRi1BHqexcVGannDr7D1pmTYWWajzHOM7bMbtB9Q==}
engines: {node: '>=18'}
sveltekit-superforms@2.23.1:
resolution: {integrity: sha512-SPj5ac4SMg8SPyP0Zi3ynwXJa7r9U1CTyn+YSyck67zLsjt367Sro4SZnl3yASrLd5kJ6Y57cgIdYJ2aWNArXw==}
peerDependencies:
'@sveltejs/kit': 1.x || 2.x
svelte: 3.x || 4.x || >=5.0.0-next.51
tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
tailwind-merge@2.5.4:
resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==}
@ -2371,23 +2532,39 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
tiny-case@1.0.3:
resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
toposort@2.0.2:
resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==}
totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
ts-algebra@2.0.0:
resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==}
ts-api-utils@2.0.0:
resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==}
engines: {node: '>=18.12'}
peerDependencies:
typescript: '>=4.8.4'
ts-deepmerge@7.0.2:
resolution: {integrity: sha512-akcpDTPuez4xzULo5NwuoKwYRtjQJ9eoNfBACiBMaXwNAx7B1PKfe5wqUFJuW5uKzQ68YjDFwPaWHDG1KnFGsA==}
engines: {node: '>=14.13.1'}
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
tslib@2.4.0:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@ -2398,6 +2575,10 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
type-fest@2.19.0:
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
engines: {node: '>=12.20'}
typescript-eslint@8.22.0:
resolution: {integrity: sha512-Y2rj210FW1Wb6TWXzQc5+P+EWI9/zdS57hLEc0gnyuvdzWo8+Y8brKlbj0muejonhMI/xAZCnZZwjbIfv1CkOw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -2425,6 +2606,21 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
valibot@0.31.1:
resolution: {integrity: sha512-2YYIhPrnVSz/gfT2/iXVTrSj92HwchCt9Cga/6hX4B26iCz9zkIsGTS0HjDYTZfTi1Un0X6aRvhBi1cfqs/i0Q==}
valibot@1.0.0-beta.11:
resolution: {integrity: sha512-Ztl5Iks1Ql7Z6CwkS5oyqguN3G8tmUiNlsHpqbDt6DLMpm+eu+n8Q7f921gI3uHvNZ8xDVkd4cEJP5t+lELOfw==}
peerDependencies:
typescript: '>=5'
peerDependenciesMeta:
typescript:
optional: true
validator@13.12.0:
resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
engines: {node: '>= 0.10'}
vite-imagetools@7.0.5:
resolution: {integrity: sha512-OOvVnaBTqJJ2J7X1cM1qpH4pj9jsfTxia1VSuWeyXtf+OnP8d0YI1LHpv8y2NT47wg+n7XiTgh3BvcSffuBWrw==}
engines: {node: '>=18.0.0'}
@ -2510,9 +2706,20 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
yup@1.6.1:
resolution: {integrity: sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==}
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
zod-to-json-schema@3.24.1:
resolution: {integrity: sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==}
peerDependencies:
zod: ^3.24.1
zod@3.24.1:
resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==}
snapshots:
'@alloc/quick-lru@5.2.0': {}
@ -2522,6 +2729,19 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
'@ark/schema@0.39.0':
dependencies:
'@ark/util': 0.39.0
optional: true
'@ark/util@0.39.0':
optional: true
'@babel/runtime@7.26.7':
dependencies:
regenerator-runtime: 0.14.1
optional: true
'@drizzle-team/brocli@0.10.2': {}
'@emnapi/runtime@1.3.1':
@ -2795,6 +3015,9 @@ snapshots:
'@eslint/core': 0.10.0
levn: 0.4.1
'@exodus/schemasafe@1.3.0':
optional: true
'@floating-ui/core@1.6.9':
dependencies:
'@floating-ui/utils': 0.2.9
@ -2810,6 +3033,23 @@ snapshots:
'@fontsource-variable/smooch-sans@5.1.1': {}
'@gcornut/valibot-json-schema@0.31.0':
dependencies:
valibot: 0.31.1
optionalDependencies:
'@types/json-schema': 7.0.15
esbuild: 0.24.2
esbuild-runner: 2.2.2(esbuild@0.24.2)
optional: true
'@hapi/hoek@9.3.0':
optional: true
'@hapi/topo@5.1.0':
dependencies:
'@hapi/hoek': 9.3.0
optional: true
'@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.6':
@ -2947,6 +3187,9 @@ snapshots:
'@polka/url@1.0.0-next.28': {}
'@poppinss/macroable@1.0.4':
optional: true
'@rollup/plugin-commonjs@28.0.2(rollup@4.31.0)':
dependencies:
'@rollup/pluginutils': 5.1.4(rollup@4.31.0)
@ -3040,6 +3283,20 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.31.0':
optional: true
'@sideway/address@4.1.5':
dependencies:
'@hapi/hoek': 9.3.0
optional: true
'@sideway/formula@3.0.1':
optional: true
'@sideway/pinpoint@2.0.0':
optional: true
'@sinclair/typebox@0.34.16':
optional: true
'@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))':
dependencies:
'@rollup/plugin-commonjs': 28.0.2(rollup@4.31.0)
@ -3134,6 +3391,23 @@ snapshots:
dependencies:
'@types/spotify-api': 0.0.25
'@types/validator@13.12.2':
optional: true
'@typeschema/class-validator@0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.1)':
dependencies:
'@typeschema/core': 0.14.0(@types/json-schema@7.0.15)
optionalDependencies:
class-validator: 0.14.1
transitivePeerDependencies:
- '@types/json-schema'
optional: true
'@typeschema/core@0.14.0(@types/json-schema@7.0.15)':
optionalDependencies:
'@types/json-schema': 7.0.15
optional: true
'@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.19.0(jiti@1.21.7))(typescript@5.7.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
@ -3211,6 +3485,21 @@ snapshots:
'@typescript-eslint/types': 8.22.0
eslint-visitor-keys: 4.2.0
'@vinejs/compiler@3.0.0':
optional: true
'@vinejs/vine@3.0.0':
dependencies:
'@poppinss/macroable': 1.0.4
'@types/validator': 13.12.2
'@vinejs/compiler': 3.0.0
camelcase: 8.0.0
dayjs: 1.11.13
dlv: 1.1.3
normalize-url: 8.0.1
validator: 13.12.0
optional: true
acorn-jsx@5.3.2(acorn@8.14.0):
dependencies:
acorn: 8.14.0
@ -3251,6 +3540,12 @@ snapshots:
aria-query@5.3.2: {}
arktype@2.0.4:
dependencies:
'@ark/schema': 0.39.0
'@ark/util': 0.39.0
optional: true
asynckit@0.4.0: {}
autoprefixer@10.4.20(postcss@8.5.1):
@ -3280,7 +3575,7 @@ snapshots:
dependencies:
file-uri-to-path: 1.0.0
bits-ui@1.0.0-next.86(svelte@5.19.1):
bits-ui@1.0.0-next.94(svelte@5.19.1):
dependencies:
'@floating-ui/core': 1.6.9
'@floating-ui/dom': 1.6.13
@ -3289,6 +3584,7 @@ snapshots:
runed: 0.23.2(svelte@5.19.1)
svelte: 5.19.1
svelte-toolbelt: 0.7.1(svelte@5.19.1)
tabbable: 6.2.0
bl@4.1.0:
dependencies:
@ -3337,6 +3633,9 @@ snapshots:
camelcase-css@2.0.1: {}
camelcase@8.0.0:
optional: true
caniuse-lite@1.0.30001695: {}
chalk@4.1.2:
@ -3362,6 +3661,13 @@ snapshots:
chownr@1.1.4: {}
class-validator@0.14.1:
dependencies:
'@types/validator': 13.12.2
libphonenumber-js: 1.11.19
validator: 13.12.0
optional: true
clsx@2.1.1: {}
color-convert@2.0.1:
@ -3404,6 +3710,9 @@ snapshots:
cssesc@3.0.0: {}
dayjs@1.11.13:
optional: true
debug@4.4.0:
dependencies:
ms: 2.1.3
@ -3450,6 +3759,11 @@ snapshots:
eastasianwidth@0.2.0: {}
effect@3.12.10:
dependencies:
fast-check: 3.23.2
optional: true
electron-to-chromium@1.5.84: {}
emoji-regex@8.0.0: {}
@ -3475,6 +3789,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
esbuild-runner@2.2.2(esbuild@0.24.2):
dependencies:
esbuild: 0.24.2
source-map-support: 0.5.21
tslib: 2.4.0
optional: true
esbuild@0.18.20:
optionalDependencies:
'@esbuild/android-arm': 0.18.20
@ -3675,6 +3996,11 @@ snapshots:
expand-template@2.0.3: {}
fast-check@3.23.2:
dependencies:
pure-rand: 6.1.0
optional: true
fast-deep-equal@3.1.3: {}
fast-glob@3.3.3:
@ -3734,6 +4060,12 @@ snapshots:
formidable@1.2.6: {}
formsnap@2.0.0(svelte@5.19.1)(sveltekit-superforms@2.23.1(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(@types/json-schema@7.0.15)(svelte@5.19.1)(typescript@5.7.3)):
dependencies:
svelte: 5.19.1
svelte-toolbelt: 0.5.0(svelte@5.19.1)
sveltekit-superforms: 2.23.1(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(@types/json-schema@7.0.15)(svelte@5.19.1)(typescript@5.7.3)
fraction.js@4.3.7: {}
fs-constants@1.0.0: {}
@ -3861,12 +4193,27 @@ snapshots:
jiti@1.21.7: {}
joi@17.13.3:
dependencies:
'@hapi/hoek': 9.3.0
'@hapi/topo': 5.1.0
'@sideway/address': 4.1.5
'@sideway/formula': 3.0.1
'@sideway/pinpoint': 2.0.0
optional: true
js-yaml@4.1.0:
dependencies:
argparse: 2.0.1
json-buffer@3.0.1: {}
json-schema-to-ts@3.1.1:
dependencies:
'@babel/runtime': 7.26.7
ts-algebra: 2.0.0
optional: true
json-schema-traverse@0.4.1: {}
json-stable-stringify-without-jsonify@1.0.1: {}
@ -3884,6 +4231,9 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
libphonenumber-js@1.11.19:
optional: true
lilconfig@2.1.0: {}
lilconfig@3.1.3: {}
@ -3910,6 +4260,8 @@ snapshots:
math-intrinsics@1.1.0: {}
memoize-weak@1.0.2: {}
merge2@1.4.1: {}
methods@1.1.2: {}
@ -3979,6 +4331,9 @@ snapshots:
normalize-range@0.1.2: {}
normalize-url@8.0.1:
optional: true
object-assign@4.1.1: {}
object-hash@3.0.0: {}
@ -4115,6 +4470,9 @@ snapshots:
prettier@3.4.2: {}
property-expr@2.0.6:
optional: true
pump@3.0.2:
dependencies:
end-of-stream: 1.4.4
@ -4122,6 +4480,9 @@ snapshots:
punycode@2.3.1: {}
pure-rand@6.1.0:
optional: true
qs@6.14.0:
dependencies:
side-channel: 1.1.0
@ -4151,6 +4512,9 @@ snapshots:
readdirp@4.1.1: {}
regenerator-runtime@0.14.1:
optional: true
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@ -4360,6 +4724,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
superstruct@2.0.2:
optional: true
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
@ -4402,6 +4769,16 @@ snapshots:
dependencies:
svelte: 5.19.1
svelte-sonner@0.3.28(svelte@5.19.1):
dependencies:
svelte: 5.19.1
svelte-toolbelt@0.5.0(svelte@5.19.1):
dependencies:
clsx: 2.1.1
style-to-object: 1.0.8
svelte: 5.19.1
svelte-toolbelt@0.7.1(svelte@5.19.1):
dependencies:
clsx: 2.1.1
@ -4426,6 +4803,35 @@ snapshots:
magic-string: 0.30.17
zimmerframe: 1.1.2
sveltekit-superforms@2.23.1(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(@types/json-schema@7.0.15)(svelte@5.19.1)(typescript@5.7.3):
dependencies:
'@sveltejs/kit': 2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))
devalue: 5.1.1
memoize-weak: 1.0.2
svelte: 5.19.1
ts-deepmerge: 7.0.2
optionalDependencies:
'@exodus/schemasafe': 1.3.0
'@gcornut/valibot-json-schema': 0.31.0
'@sinclair/typebox': 0.34.16
'@typeschema/class-validator': 0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.1)
'@vinejs/vine': 3.0.0
arktype: 2.0.4
class-validator: 0.14.1
effect: 3.12.10
joi: 17.13.3
json-schema-to-ts: 3.1.1
superstruct: 2.0.2
valibot: 1.0.0-beta.11(typescript@5.7.3)
yup: 1.6.1
zod: 3.24.1
zod-to-json-schema: 3.24.1(zod@3.24.1)
transitivePeerDependencies:
- '@types/json-schema'
- typescript
tabbable@6.2.0: {}
tailwind-merge@2.5.4: {}
tailwind-merge@2.6.0: {}
@ -4489,18 +4895,32 @@ snapshots:
dependencies:
any-promise: 1.3.0
tiny-case@1.0.3:
optional: true
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
toposort@2.0.2:
optional: true
totalist@3.0.1: {}
ts-algebra@2.0.0:
optional: true
ts-api-utils@2.0.0(typescript@5.7.3):
dependencies:
typescript: 5.7.3
ts-deepmerge@7.0.2: {}
ts-interface-checker@0.1.13: {}
tslib@2.4.0:
optional: true
tslib@2.8.1: {}
tunnel-agent@0.6.0:
@ -4511,6 +4931,9 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
type-fest@2.19.0:
optional: true
typescript-eslint@8.22.0(eslint@9.19.0(jiti@1.21.7))(typescript@5.7.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.19.0(jiti@1.21.7))(typescript@5.7.3)
@ -4537,6 +4960,17 @@ snapshots:
util-deprecate@1.0.2: {}
valibot@0.31.1:
optional: true
valibot@1.0.0-beta.11(typescript@5.7.3):
optionalDependencies:
typescript: 5.7.3
optional: true
validator@13.12.0:
optional: true
vite-imagetools@7.0.5(rollup@4.31.0):
dependencies:
'@rollup/pluginutils': 5.1.4(rollup@4.31.0)
@ -4586,4 +5020,19 @@ snapshots:
yocto-queue@0.1.0: {}
yup@1.6.1:
dependencies:
property-expr: 2.0.6
tiny-case: 1.0.3
toposort: 2.0.2
type-fest: 2.19.0
optional: true
zimmerframe@1.1.2: {}
zod-to-json-schema@3.24.1(zod@3.24.1):
dependencies:
zod: 3.24.1
optional: true
zod@3.24.1: {}

View file

@ -1,6 +1,25 @@
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { relations } from 'drizzle-orm/relations';
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { nanoid } from 'nanoid';
export const user = sqliteTable('user', {
id: integer('id').primaryKey(),
age: integer('age')
export const questions = sqliteTable('questions', {
id: text('id')
.primaryKey()
.$defaultFn(() => nanoid()),
creator: text('creator').notNull(), // session token
content: text('content').notNull().unique()
});
export const answers = sqliteTable('answers', {
id: text('id')
.primaryKey()
.$defaultFn(() => nanoid()),
creator: text('creator').notNull(), // session token
content: text('content').notNull(),
questionId: text('question_id').references(() => questions.id)
});
// Relations
export const questionsRelations = relations(questions, ({ many }) => ({
answers: many(answers)
}));

View file

@ -1,4 +1,5 @@
import type { Picture } from 'vite-imagetools';
import type { answers, questions } from './server/db/schema';
export type EnhancedImage = {
src: string | Picture;
@ -75,3 +76,6 @@ export type Tag = {
};
export type TagsObj = Record<string, Tag>;
export type Answer = typeof answers.$inferSelect;
export type Question = typeof questions.$inferSelect & { answers: Answer[] };

View file

@ -0,0 +1,72 @@
import { json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { questions, answers } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
const maxAnswers = 5;
export async function POST({ locals, request }) {
const { userId, questionId, content }: { userId: string; questionId: string; content: string } =
await request.json();
const { session } = locals;
if (!session?.data?.userId) {
return;
}
const user = session.data.userId;
if (!user || !userId || user !== userId) {
return json({ error: 'Unauthorized' }, { status: 401 });
}
if (!content) {
return json({ error: 'Answer is required' }, { status: 400 });
}
if (!questionId) {
return json({ error: 'No question specified' }, { status: 400 });
}
const question = await db
.select({
question: questions
})
.from(questions)
.where(eq(questions.id, questionId))
.limit(1);
if (!question) {
return json({ error: 'Question not found' }, { status: 400 });
}
if (question[0].question.creator === user) {
return json({ error: 'Not allowed to answer your own question' }, { status: 400 });
}
const currentAnswers = await db
.select({
answer: answers
})
.from(answers)
.where(eq(answers.questionId, questionId));
if (currentAnswers.length >= maxAnswers) {
return json({ error: 'No more answers needed for this question' }, { status: 400 });
}
try {
const [newAnswer] = await db
.insert(answers)
.values({
content: content,
creator: userId,
questionId: questionId
})
.returning();
return json(newAnswer);
} catch (e) {
return json({ error: e }, { status: 400 });
}
}

View file

@ -0,0 +1,52 @@
import { json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { questions, answers } from '$lib/server/db/schema';
import { eq, sql } from 'drizzle-orm';
import type { Question } from '$lib/types';
export async function GET({ params }) {
const limit = parseInt(params.limit) || 10;
const offset = parseInt(params.offset) || 0;
// Get total count
const [{ total }] = await db
.select({
total: sql`count(DISTINCT ${questions.id})`
})
.from(questions)
.innerJoin(answers, eq(questions.id, answers.questionId));
const results = await db
.select({
question: questions,
answers: answers
})
.from(questions)
.innerJoin(answers, eq(questions.id, answers.questionId))
.limit(limit)
.offset(offset);
// Group answers by question
type QuestionMap = {
[key: string]: Question;
};
const questionsWithAnswers = results.reduce<QuestionMap>((acc, row) => {
const questionId = row.question.id;
if (!acc[questionId]) {
acc[questionId] = {
...row.question,
answers: []
};
}
if (row.answers) {
acc[questionId].answers.push(row.answers);
}
return acc;
}, {});
return json({ data: Object.values(questionsWithAnswers), meta: { limit, offset, total } });
}

View file

@ -0,0 +1,67 @@
import { json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { questions, answers } from '$lib/server/db/schema';
import { eq, count, and, not, sql } from 'drizzle-orm';
export async function GET({ locals }) {
const { session } = locals;
if (!session?.data?.userId) {
return;
}
const user = session.data.userId;
const questionWithAnswerCount = await db
.select({
id: questions.id,
content: questions.content,
answerCount: count(answers.id)
})
.from(questions)
.leftJoin(answers, eq(questions.id, answers.questionId))
.groupBy(questions.id)
.having(and(sql`${count(answers.id)} < 5`, not(eq(questions.creator, user))))
.orderBy(sql`RANDOM()`)
.limit(1);
if (!questionWithAnswerCount.length) {
return json({ error: 'No questions available' }, { status: 404 });
}
return json(questionWithAnswerCount[0]);
}
export async function POST({ locals, request }) {
const { userId, content }: { userId: string; content: string } = await request.json();
const { session } = locals;
if (!session?.data?.userId) {
return;
}
const user = session.data.userId;
if (!user || !userId || user !== userId) {
console.log(user, userId);
return json({ error: 'Unauthorized' }, { status: 401 });
}
if (!content) {
return json({ error: 'Content is required' }, { status: 400 });
}
try {
const [newQuestion] = await db
.insert(questions)
.values({
content: content.at(-1) === '?' ? content.slice(0, -1) : content,
creator: userId
})
.returning();
return json(newQuestion);
} catch (e) {
return json({ error: e }, { status: 400 });
}
}

View file

@ -1,6 +1,8 @@
<script lang="ts">
import { baseURL, stuffSite } from '$lib/config.js';
import { Toaster } from '$lib/components/ui/sonner/index.js';
let { children, data } = $props();
const title = data?.name ? `${data.name} | ${stuffSite.name}` : stuffSite.name;
@ -18,6 +20,8 @@
<meta property="og:image" content={baseURL + ogImage} />
</svelte:head>
<Toaster />
{@render children()}
{#if data?.name === 'Vau kui vali'}

View file

@ -0,0 +1,162 @@
import type { Actions, PageServerLoad } from './$types';
import type { Question } from '$lib/types';
import { formSchema as questionSchema } from './question-schema';
import { formSchema as answerSchema } from './answer-schema';
import { superValidate } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import { nanoid } from 'nanoid';
import { fail } from '@sveltejs/kit';
const perPage = 5;
export const load: PageServerLoad = async ({ fetch, locals }) => {
const { session } = locals;
if (!session?.data?.userId) {
await session.setData({ userId: nanoid() });
await session.save();
}
const user = session.data.userId;
const res = await fetch('/api/rahvatarkus/question')
.then(async (res) => {
const data = await res.json();
return { ok: res.ok, data: data };
})
.then((data) => {
return data;
});
let question: Question | undefined = undefined;
if (res.ok) {
question = res.data;
}
const archiveRes = fetch(`/api/rahvatarkus/archive/${perPage}`)
.then((res) => {
return res.json();
})
.then((data) => {
return data;
});
return {
user: user,
question: question,
question_form: await superValidate(zod(questionSchema)),
answer_form: await superValidate({ questionId: question?.id }, zod(answerSchema), {
errors: false
}),
perPage,
streamed: {
archive: archiveRes
}
};
};
export const actions: Actions = {
answer: async (event) => {
const { session } = event.locals;
if (!session?.data?.userId) {
return;
}
const user = session.data.userId;
const form = await superValidate(event, zod(answerSchema));
if (!form.valid) {
return fail(400, {
form
});
}
const response = await event
.fetch('/api/rahvatarkus/answer', {
method: 'POST',
body: JSON.stringify({
userId: user,
content: form.data.answer,
questionId: form.data.questionId
})
})
.then(async (res) => {
const data = await res.json();
return { ok: res.ok, data: data };
})
.then((data) => {
return data;
});
if (!response.ok) {
const errorMessage = response.data?.error;
if (form.errors.answer) {
form.errors.answer.push(errorMessage);
} else {
form.errors.answer = [errorMessage];
}
return fail(400, {
form
});
}
return {
form
};
},
question: async (event) => {
const { session } = event.locals;
if (!session?.data?.userId) {
return;
}
const user = session.data.userId;
const form = await superValidate(event, zod(questionSchema));
if (!form.valid) {
return fail(400, {
form
});
}
const response = await event
.fetch('/api/rahvatarkus/question', {
method: 'POST',
body: JSON.stringify({ userId: user, content: form.data.question })
})
.then(async (res) => {
const data = await res.json();
return { ok: res.ok, data: data };
})
.then((data) => {
return data;
});
if (!response.ok) {
if (response.data?.error?.code === 'SQLITE_CONSTRAINT_UNIQUE') {
const errorMessage = 'Sellel küsimusel on juba vastus.';
if (form.errors.question) {
form.errors.question.push(errorMessage);
} else {
form.errors.question = [errorMessage];
}
}
return fail(400, {
form
});
}
return {
form
};
}
};

View file

@ -0,0 +1,63 @@
<script lang="ts">
import type { PageData } from './$types.js';
import * as Accordion from '$lib/components/ui/accordion/index.js';
import * as Pagination from '$lib/components/ui/pagination/index.js';
import QuestionForm from './question-form.svelte';
import AnswerForm from './answer-form.svelte';
import SuperDebug from 'sveltekit-superforms';
let { data }: { data: PageData } = $props();
$inspect(data);
</script>
<QuestionForm {data} />
<AnswerForm {data} />
{#await data.streamed.archive}
<p>loading</p>
{:then archive}
<Accordion.Root type="multiple" class="w-2/3 space-y-6">
{#each archive.data as question}
<Accordion.Item disabled={!(question.answers?.length > 0)} value={question.id}>
<Accordion.Trigger>{question.content}?</Accordion.Trigger>
<Accordion.Content>
<ol class="ml-6 list-decimal [&>li]:mt-2">
{#each question.answers as answer}
<li>
{answer.content}
</li>
{/each}
</ol>
</Accordion.Content>
</Accordion.Item>
{/each}
</Accordion.Root>
<Pagination.Root count={archive.meta.total} perPage={data.perPage}>
{#snippet children({ pages, currentPage })}
<Pagination.Content>
<Pagination.Item>
<Pagination.PrevButton />
</Pagination.Item>
{#each pages as page (page.key)}
{#if page.type === 'ellipsis'}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
{:else}
<Pagination.Item isVisible={currentPage === page.value}>
<Pagination.Link {page} isActive={currentPage === page.value}>
{page.value}
</Pagination.Link>
</Pagination.Item>
{/if}
{/each}
<Pagination.Item>
<Pagination.NextButton />
</Pagination.Item>
</Pagination.Content>
{/snippet}
</Pagination.Root>
{/await}

View file

@ -0,0 +1,50 @@
<script lang="ts">
import { formSchema, type FormSchema } from './answer-schema';
import type { Question } from '$lib/types';
import { toast } from 'svelte-sonner';
import { zodClient } from 'sveltekit-superforms/adapters';
import { type SuperValidated, type Infer, superForm } from 'sveltekit-superforms';
import * as Form from '$lib/components/ui/form/index.js';
import { Input } from '$lib/components/ui/input/index.js';
let { data }: { data: { answer_form: SuperValidated<Infer<FormSchema>>; question: Question } } =
$props();
const form = superForm(data.answer_form, {
validators: zodClient(formSchema),
onUpdated: ({ form: f }) => {
if (f.valid) {
toast.success(`You submitted ${JSON.stringify(f.data, null, 2)}`);
} else {
toast.error('Please fix the errors in the form.');
}
}
});
const { form: formData, enhance } = form;
</script>
{#if data.question}
<form method="POST" class="w-2/3 space-y-6" use:enhance action="?/answer">
<Form.Field {form} name="answer">
<Form.Control>
{#snippet children({ props })}
<Form.Label>{data.question.content}?</Form.Label>
<Input {...props} bind:value={$formData.answer} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field {form} name="questionId">
<Form.Control>
{#snippet children({ props })}
<Input type="hidden" {...props} bind:value={$formData.questionId} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Button>Vasta</Form.Button>
</form>
{/if}

View file

@ -0,0 +1,8 @@
import { z } from 'zod';
export const formSchema = z.object({
questionId: z.string().length(21),
answer: z.string().min(2).max(250)
});
export type FormSchema = typeof formSchema;

View file

@ -0,0 +1,39 @@
<script lang="ts">
import { formSchema, type FormSchema } from './question-schema';
import { toast } from 'svelte-sonner';
import { zodClient } from 'sveltekit-superforms/adapters';
import { type SuperValidated, type Infer, superForm } from 'sveltekit-superforms';
import * as Form from '$lib/components/ui/form/index.js';
import { Input } from '$lib/components/ui/input/index.js';
let { data }: { data: { question_form: SuperValidated<Infer<FormSchema>> } } = $props();
const form = superForm(data.question_form, {
validators: zodClient(formSchema),
onUpdated: ({ form: f }) => {
if (f.valid) {
toast.success(`You submitted ${JSON.stringify(f.data, null, 2)}`);
} else {
toast.error('Please fix the errors in the form.');
}
}
});
const { form: formData, enhance } = form;
</script>
<form method="POST" class="w-2/3 space-y-6" use:enhance action="?/question">
<Form.Field {form} name="question">
<Form.Control>
{#snippet children({ props })}
<Form.Label>Uus küsimus</Form.Label>
<Input {...props} bind:value={$formData.question} />
{/snippet}
</Form.Control>
<Form.Description>Küsi ükskõik mida sellelt kollektiiv intelektilt.</Form.Description>
<Form.FieldErrors />
</Form.Field>
<Form.Button>Küsi</Form.Button>
</form>

View file

@ -0,0 +1,7 @@
import { z } from 'zod';
export const formSchema = z.object({
question: z.string().min(2).max(50)
});
export type FormSchema = typeof formSchema;