2025-02-21

This commit is contained in:
gcch 2025-02-21 23:23:22 +01:00
commit 5d5918f0d7
69 changed files with 1481 additions and 305 deletions

14
.zed/settings.json Normal file
View file

@ -0,0 +1,14 @@
{
"language_servers": ["!vtsls", "!deno", "!tailwindcss-language-server", "!biome", "..."],
"languages": {
"Vue.js": {
"code_actions_on_format": {
"source.fixAll.eslint": true,
"source.organizeImports": true
},
"formatter": "language_server",
"prettier": null,
"format_on_save": "on"
}
}
}

233
bun.lock
View file

@ -4,43 +4,50 @@
"": {
"name": "journal-media",
"dependencies": {
"@effect/platform": "^0.77.2",
"@effect/sql-drizzle": "^0.29.2",
"a11y-dialog": "^8.1.1",
"drizzle-orm": "^0.39.3",
"effect": "^3.13.2",
"pinia": "^3.0.1",
"sqlocal": "^0.14.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"@effect/platform": "latest",
"@effect/sql-drizzle": "latest",
"@unhead/vue": "latest",
"@vueuse/core": "latest",
"@vueuse/router": "latest",
"a11y-dialog": "latest",
"drizzle-orm": "latest",
"effect": "latest",
"pinia": "latest",
"sqlocal": "latest",
"unhead": "latest",
"vue": "latest",
"vue-router": "latest",
},
"devDependencies": {
"@types/bun": "^1.2.2",
"@types/node": "^22.13.4",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.4.0",
"browserlist": "^1.0.1",
"browserslist": "^4.24.4",
"drizzle-kit": "^0.30.4",
"eslint": "^9.20.1",
"eslint-plugin-perfectionist": "^4.9.0",
"eslint-plugin-vue": "^9.32.0",
"globals": "^15.15.0",
"jiti": "^2.4.2",
"lightningcss": "^1.29.1",
"prettier": "^3.5.1",
"typescript": "^5.7.3",
"vite": "^6.1.1",
"vite-plugin-vue-devtools": "^7.7.2",
"vue-tsc": "^2.2.2",
"@types/bun": "latest",
"@unhead/addons": "latest",
"@vitejs/plugin-vue": "latest",
"@vue/eslint-config-typescript": "latest",
"@vue/typescript-plugin": "latest",
"browserslist": "latest",
"drizzle-kit": "latest",
"eslint": "latest",
"eslint-plugin-perfectionist": "latest",
"eslint-plugin-vue": "latest",
"globals": "latest",
"jiti": "latest",
"knip": "latest",
"lightningcss": "latest",
"prettier": "latest",
"prettier-plugin-pkg": "latest",
"prettier-plugin-sh": "latest",
"tsr": "latest",
"typescript": "latest",
"vite": "latest",
"vite-plugin-vue-devtools": "latest",
"vue-tsc": "latest",
},
},
},
"packages": {
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
"@antfu/utils": ["@antfu/utils@0.7.10", "", {}, "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww=="],
"@antfu/utils": ["@antfu/utils@8.1.1", "", {}, "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ=="],
"@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
@ -170,15 +177,15 @@
"@eslint/config-array": ["@eslint/config-array@0.19.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="],
"@eslint/core": ["@eslint/core@0.11.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA=="],
"@eslint/core": ["@eslint/core@0.12.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg=="],
"@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/eslintrc": ["@eslint/eslintrc@3.3.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-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ=="],
"@eslint/js": ["@eslint/js@9.20.0", "", {}, "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ=="],
"@eslint/js": ["@eslint/js@9.21.0", "", {}, "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.6", "", { "dependencies": { "@eslint/core": "^0.11.0", "levn": "^0.4.1" } }, "sha512-+0TjwR1eAUdZtvv/ir1mGX+v0tUoR3VEPB8Up0LLJC+whRW0GgBBtpbOkg/a/U4Dxa6l5a3l9AJ1aWIQVyoWJA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.7", "", { "dependencies": { "@eslint/core": "^0.12.0", "levn": "^0.4.1" } }, "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@ -186,7 +193,7 @@
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.1", "", {}, "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
@ -210,16 +217,14 @@
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@4.0.1", "", { "dependencies": { "@nodelib/fs.stat": "4.0.0", "run-parallel": "^1.2.0" } }, "sha512-vAkI715yhnmiPupY+dq+xenu5Tdf2TBQ66jLvBIcCddtz+5Q8LbMKaf9CIJJreez8fQ8fgaY+RaywQx8RJIWpw=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@nodelib/fs.walk": ["@nodelib/fs.walk@3.0.1", "", { "dependencies": { "@nodelib/fs.scandir": "4.0.1", "fastq": "^1.15.0" } }, "sha512-nIh/M6Kh3ZtOmlY00DaUYB4xeeV6F3/ts1l29iwl3/cfyY/OuCfUx+v08zgx8TKPTifXRcjjqVQ4KB2zOYSbyw=="],
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.30.0", "", {}, "sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw=="],
"@pkgr/core": ["@pkgr/core@0.1.1", "", {}, "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA=="],
"@polka/url": ["@polka/url@1.0.0-next.28", "", {}, "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw=="],
"@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=="],
@ -266,6 +271,8 @@
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
"@snyk/github-codeowners": ["@snyk/github-codeowners@1.1.0", "", { "dependencies": { "commander": "^4.1.1", "ignore": "^5.1.8", "p-map": "^4.0.0" }, "bin": { "github-codeowners": "dist/cli.js" } }, "sha512-lGFf08pbkEac0NYgVf4hdANpAgApRjNByLXB+WBip3qj1iendOIyAwP2GKkKbQMNVy2r1xxDf0ssfWscoiC+Vw=="],
"@sqlite.org/sqlite-wasm": ["@sqlite.org/sqlite-wasm@3.48.0-build4", "", { "bin": { "sqlite-wasm": "bin/index.js" } }, "sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ=="],
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
@ -278,6 +285,8 @@
"@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="],
"@types/web-bluetooth": ["@types/web-bluetooth@0.0.20", "", {}, "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="],
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.24.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.24.1", "@typescript-eslint/type-utils": "8.24.1", "@typescript-eslint/utils": "8.24.1", "@typescript-eslint/visitor-keys": "8.24.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "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-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA=="],
@ -300,6 +309,10 @@
"@ungap/with-resolvers": ["@ungap/with-resolvers@0.1.0", "", {}, "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="],
"@unhead/addons": ["@unhead/addons@2.0.0-alpha.19", "", { "dependencies": { "@rollup/pluginutils": "^5.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17", "mlly": "^1.7.4", "ufo": "^1.5.4", "unplugin": "^2.2.0", "unplugin-ast": "^0.14.0" }, "peerDependencies": { "unhead": "2.0.0-alpha.19" } }, "sha512-nzTC9/J6SJ425YSFZoC2Hf1w3JbmGO3zvT4CsY9/RF0ByihxsIH63v84nVN/K0hAm1CeOVho53yWH1lTgcNksQ=="],
"@unhead/vue": ["@unhead/vue@2.0.0-alpha.19", "", { "dependencies": { "hookable": "^5.5.3", "unhead": "2.0.0-alpha.19" }, "peerDependencies": { "vue": ">=3.5.13" } }, "sha512-PEjFg2oYXKZOGnR75ANReudH+/OdRlQkE8jwf9KBFLXiuu94tdqyph3gcgUs0pHsZmgVYpwxvdkOg+lldBUpjA=="],
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.1", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ=="],
"@volar/language-core": ["@volar/language-core@2.4.11", "", { "dependencies": { "@volar/source-map": "2.4.11" } }, "sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg=="],
@ -332,8 +345,6 @@
"@vue/devtools-shared": ["@vue/devtools-shared@7.7.2", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA=="],
"@vue/eslint-config-prettier": ["@vue/eslint-config-prettier@10.2.0", "", { "dependencies": { "eslint-config-prettier": "^10.0.1", "eslint-plugin-prettier": "^5.2.2" }, "peerDependencies": { "eslint": ">= 8.21.0", "prettier": ">= 3.0.0" } }, "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw=="],
"@vue/eslint-config-typescript": ["@vue/eslint-config-typescript@14.4.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.23.0", "fast-glob": "^3.3.3", "typescript-eslint": "^8.23.0", "vue-eslint-parser": "^9.4.3" }, "peerDependencies": { "eslint": "^9.10.0", "eslint-plugin-vue": "^9.28.0", "typescript": ">=4.8.4" }, "optionalPeers": ["typescript"] }, "sha512-daU+eAekEeVz3CReE4PRW25fe+OJDKwE28jHN6LimDEnuFMbJ6H4WGogEpNof276wVP6UvzOeJQfLFjB5mW29A=="],
"@vue/language-core": ["@vue/language-core@2.2.2", "", { "dependencies": { "@volar/language-core": "~2.4.11", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^1.0.3", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-QotO41kurE5PLf3vrNgGTk3QswO2PdUFjBwNiOi7zMmGhwb25PSTh9hD1MCgKC06AVv+8sZQvlL3Do4TTVHSiQ=="],
@ -348,20 +359,36 @@
"@vue/shared": ["@vue/shared@3.5.13", "", {}, "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="],
"@vue/typescript-plugin": ["@vue/typescript-plugin@2.2.2", "", { "dependencies": { "@volar/typescript": "~2.4.11", "@vue/language-core": "2.2.2", "@vue/shared": "^3.5.0" } }, "sha512-M/fNR/M4Rt+jm7Vmv21sPoHNM7MLYC7QjW5gqmP9y6HGmIlxOubqXYIqddeEnikmEsOc3linykEAxAIVxfIvbA=="],
"@vueuse/core": ["@vueuse/core@12.7.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "12.7.0", "@vueuse/shared": "12.7.0", "vue": "^3.5.13" } }, "sha512-jtK5B7YjZXmkGNHjviyGO4s3ZtEhbzSgrbX+s5o+Lr8i2nYqNyHuPVOeTdM1/hZ5Tkxg/KktAuAVDDiHMraMVA=="],
"@vueuse/metadata": ["@vueuse/metadata@12.7.0", "", {}, "sha512-4VvTH9mrjXqFN5LYa5YfqHVRI6j7R00Vy4995Rw7PQxyCL3z0Lli86iN4UemWqixxEvYfRjG+hF9wL8oLOn+3g=="],
"@vueuse/router": ["@vueuse/router@12.7.0", "", { "dependencies": { "@vueuse/shared": "12.7.0", "vue": "^3.5.13" }, "peerDependencies": { "vue-router": ">=4.0.0-rc.1" } }, "sha512-Jp6dIel54oc2nh++zqjY06ipCcTT6YWDCNQ8dSSnqRwx90wIl7w7MQP7Wpp1wrDwXEoqhelfeZf2gjfrkAhq3g=="],
"@vueuse/shared": ["@vueuse/shared@12.7.0", "", { "dependencies": { "vue": "^3.5.13" } }, "sha512-coLlUw2HHKsm7rPN6WqHJQr18WymN4wkA/3ThFaJ4v4gWGWAQQGK+MJxLuJTBs4mojQiazlVWAKNJNpUWGRkNw=="],
"a11y-dialog": ["a11y-dialog@8.1.1", "", { "dependencies": { "focusable-selectors": "^0.8.0" } }, "sha512-7SBLXFwhQBnEHOaIiKUUQZ5VKJa/b1jBDvPJvlejlqX2w9cpi+iHBrdjcmd4Xd6vIdsuMHGo9Is2SWu0Hzu0zg=="],
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"alien-signals": ["alien-signals@1.0.3", "", {}, "sha512-zQOh3wAYK5ujENxvBBR3CFGF/b6afaSzZ/c9yNhJ1ENrGHETvpUuKQsa93Qrclp0+PzTF93MaZ7scVp1uUozhA=="],
"ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"ast-kit": ["ast-kit@1.4.0", "", { "dependencies": { "@babel/parser": "^7.26.5", "pathe": "^2.0.2" } }, "sha512-BlGeOw73FDsX7z0eZE/wuuafxYoek2yzNJ6l6A1nsb4+z/p87TOPbHaWuN53kFKNuUXiCQa2M+xLF71IqQmRSw=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"birpc": ["birpc@0.2.19", "", {}, "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ=="],
@ -372,8 +399,6 @@
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"browserlist": ["browserlist@1.0.1", "", { "dependencies": { "chalk": "^2.4.1" }, "bin": { "browserlist": "./cli.js" } }, "sha512-nYq9jiWv+qXcgrJxQzivfEc7Wo2GvAKkeRViE5L3cUJpq4SZO6NZR710I/8T+OjE5BPECbzpm8rpUkwslE3nTg=="],
"browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
@ -386,16 +411,24 @@
"caniuse-lite": ["caniuse-lite@1.0.30001700", "", {}, "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ=="],
"chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
"clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
"coincident": ["coincident@1.2.3", "", { "dependencies": { "@ungap/structured-clone": "^1.2.0", "@ungap/with-resolvers": "^0.1.0", "gc-hook": "^0.3.1", "proxy-target": "^3.0.2" }, "optionalDependencies": { "ws": "^8.16.0" } }, "sha512-Uxz3BMTWIslzeWjuQnizGWVg0j6khbvHUQ8+5BdM7WuJEm4ALXwq3wluYoB+uF68uPBz/oUOeJnYURKyfjexlA=="],
"color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
"copy-anything": ["copy-anything@3.0.5", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="],
@ -416,6 +449,8 @@
"default-browser-id": ["default-browser-id@5.0.0", "", {}, "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA=="],
"defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="],
"define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
@ -424,10 +459,14 @@
"drizzle-orm": ["drizzle-orm@0.39.3", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-EZ8ZpYvDIvKU9C56JYLOmUskazhad+uXZCTCRN4OnRMsL+xAJ05dv1eCpAG5xzhsm1hqiuC5kAZUCS924u2DTw=="],
"easy-table": ["easy-table@1.2.0", "", { "dependencies": { "ansi-regex": "^5.0.1" }, "optionalDependencies": { "wcwidth": "^1.0.1" } }, "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww=="],
"effect": ["effect@3.13.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-/w+CPqHDJ33Wq7xC4YKAchrEEPtjvxh563xH9kDTZp99seNYBoBs87vl8DJwartEjj+KLQLP8PzoDne+XmGT2A=="],
"electron-to-chromium": ["electron-to-chromium@1.5.102", "", {}, "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q=="],
"enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"error-stack-parser-es": ["error-stack-parser-es@0.1.5", "", {}, "sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg=="],
@ -440,14 +479,10 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.20.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.20.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-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g=="],
"eslint-config-prettier": ["eslint-config-prettier@10.0.1", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "build/bin/cli.js" } }, "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw=="],
"eslint": ["eslint@9.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.0", "@eslint/js": "9.21.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@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-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg=="],
"eslint-plugin-perfectionist": ["eslint-plugin-perfectionist@4.9.0", "", { "dependencies": { "@typescript-eslint/types": "^8.24.0", "@typescript-eslint/utils": "^8.24.0", "natural-orderby": "^5.0.0" }, "peerDependencies": { "eslint": ">=8.0.0" } }, "sha512-76lDfJnonOcXGW3bEXuqhEGId0LrOlvIE1yLHvK/eKMMPOc0b43KchAIR2Bdbqlg+LPXU5/Q+UzuzkO+cWHT6w=="],
"eslint-plugin-prettier": ["eslint-plugin-prettier@5.2.3", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.9.1" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "optionalPeers": ["@types/eslint", "eslint-config-prettier"] }, "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw=="],
"eslint-plugin-vue": ["eslint-plugin-vue@9.32.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "globals": "^13.24.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", "semver": "^7.6.3", "vue-eslint-parser": "^9.4.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug=="],
"eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="],
@ -462,7 +497,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=="],
@ -472,8 +507,6 @@
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
@ -512,13 +545,13 @@
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="],
"globals": ["globals@16.0.0", "", {}, "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
"has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
@ -534,6 +567,8 @@
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
@ -576,6 +611,8 @@
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"knip": ["knip@5.44.4", "", { "dependencies": { "@nodelib/fs.walk": "3.0.1", "@snyk/github-codeowners": "1.1.0", "easy-table": "1.2.0", "enhanced-resolve": "^5.18.0", "fast-glob": "^3.3.3", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "minimist": "^1.2.8", "picocolors": "^1.1.0", "picomatch": "^4.0.1", "pretty-ms": "^9.0.0", "smol-toml": "^1.3.1", "strip-json-comments": "5.0.1", "summary": "2.1.0", "zod": "^3.22.4", "zod-validation-error": "^3.0.3" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-Ryn8LwWHLId8jSK1DgtT0hmg5DbzkqAtH+Gg3vZJpmSMgGHMspej9Ag+qKTm8wsPLDjVetuEz/lIsobo0XCMvQ=="],
"kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
@ -612,14 +649,22 @@
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"magic-string-ast": ["magic-string-ast@0.7.0", "", { "dependencies": { "magic-string": "^0.30.17" } }, "sha512-686fgAHaJY7wLTFEq7nnKqeQrhqmXB19d1HnqT35Ci7BN6hbAYLZUezTQ062uUHM7ggZEQlqJ94Ftls+KDXU8Q=="],
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
"mlly": ["mlly@1.7.4", "", { "dependencies": { "acorn": "^8.14.0", "pathe": "^2.0.1", "pkg-types": "^1.3.0", "ufo": "^1.5.4" } }, "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw=="],
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
@ -632,6 +677,8 @@
"multipasta": ["multipasta@0.2.5", "", {}, "sha512-c8eMDb1WwZcE02WVjHoOmUVk7fnKU/RmUcosHACglrWAuPQsEJv+E8430sXj6jNc1jHw0zrS16aCjQh4BcEb4A=="],
"mvdan-sh": ["mvdan-sh@0.10.1", "", {}, "sha512-kMbrH0EObaKmK3nVRKUIIya1dpASHIEusM13S4V1ViHFuxuNxCo+arxoa6j/dbV22YBGjl7UKJm9QQKJ2Crzhg=="],
"nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
@ -654,6 +701,8 @@
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
@ -670,10 +719,12 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"pinia": ["pinia@3.0.1", "", { "dependencies": { "@vue/devtools-api": "^7.7.2" }, "peerDependencies": { "typescript": ">=4.4.4", "vue": "^2.7.0 || ^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg=="],
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
"postcss": ["postcss@8.5.2", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA=="],
"postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
@ -682,7 +733,9 @@
"prettier": ["prettier@3.5.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw=="],
"prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="],
"prettier-plugin-pkg": ["prettier-plugin-pkg@0.18.1", "", { "peerDependencies": { "prettier": "^3.0.3" } }, "sha512-FuUxvsYZR/8rsLH8s/jbPQmgYvv0yxW8LoIHCy6+Q7p4FBjjdP3DNKx8fMTOsc0SlEB1skB4o1LcahRceIh87A=="],
"prettier-plugin-sh": ["prettier-plugin-sh@0.15.0", "", { "dependencies": { "mvdan-sh": "^0.10.1", "sh-syntax": "^0.4.2" }, "peerDependencies": { "prettier": "^3.0.3" } }, "sha512-U0PikJr/yr2bzzARl43qI0mApBj0C1xdAfA04AZa6LnvIKawXHhuy2fFo6LNA7weRzGlAiNbaEFfKMFo0nZr/A=="],
"pretty-ms": ["pretty-ms@9.2.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg=="],
@ -710,6 +763,8 @@
"semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"sh-syntax": ["sh-syntax@0.4.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-/l2UZ5fhGZLVZa16XQM9/Vq/hezGGbdHeVEA01uWjOL1+7Ek/gt6FquW0iKKws4a9AYPYvlz6RyVvjh3JxOteg=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
@ -718,6 +773,8 @@
"sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="],
"smol-toml": ["smol-toml@1.3.1", "", {}, "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ=="],
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
@ -730,15 +787,17 @@
"strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"strip-json-comments": ["strip-json-comments@5.0.1", "", {}, "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw=="],
"summary": ["summary@2.1.0", "", {}, "sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw=="],
"superjson": ["superjson@2.2.2", "", { "dependencies": { "copy-anything": "^3.0.2" } }, "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q=="],
"supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"svg-tags": ["svg-tags@1.0.0", "", {}, "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA=="],
"synckit": ["synckit@0.9.2", "", { "dependencies": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" } }, "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw=="],
"tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
@ -748,6 +807,8 @@
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tsr": ["tsr@1.3.4", "", { "dependencies": { "mri": "^1.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "typescript": ">=4.0.0" }, "bin": { "tsr": "dist/cli.js" } }, "sha512-VHGK2GNokLaQ2OXdL4kE8+U9Dn6EWmDEZ/HdoFCnOoyR6ZFxi0QdzshPs7ZJ1xY9lfi+pQ0hZI5fLZAHZIJ+GA=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="],
@ -756,12 +817,22 @@
"typescript-eslint": ["typescript-eslint@8.24.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.24.1", "@typescript-eslint/parser": "8.24.1", "@typescript-eslint/utils": "8.24.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA=="],
"ufo": ["ufo@1.5.4", "", {}, "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ=="],
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
"unhead": ["unhead@2.0.0-alpha.19", "", { "dependencies": { "hookable": "^5.5.3" } }, "sha512-MbjBinF3G61+Ecy/eQt013SCdnIdUhu14JKyDf7fAzpV0d6snC6BdPY/O+NOk4MNWpxrD05Dc8/AJ/8Vpo66IQ=="],
"unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
"unplugin": ["unplugin@2.2.0", "", { "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" } }, "sha512-m1ekpSwuOT5hxkJeZGRxO7gXbXT3gF26NjQ7GdVHoLoF8/nopLcd/QfPigpCy7i51oFHiRJg/CyHhj4vs2+KGw=="],
"unplugin-ast": ["unplugin-ast@0.14.0", "", { "dependencies": { "@antfu/utils": "^8.1.0", "@babel/generator": "^7.26.5", "ast-kit": "^1.4.0", "magic-string-ast": "^0.7.0", "unplugin": "^2.1.2", "unplugin-utils": "^0.2.0" } }, "sha512-a9MPyEyrecxmaMGS7xmwYVXcdZIAPaAr+ePmPR1duTh3Bk6/QxR70udb4dkvggZzy9AuzD/7b0RVCcA/N9HmzQ=="],
"unplugin-utils": ["unplugin-utils@0.2.4", "", { "dependencies": { "pathe": "^2.0.2", "picomatch": "^4.0.2" } }, "sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA=="],
"update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
@ -790,6 +861,10 @@
"vue-tsc": ["vue-tsc@2.2.2", "", { "dependencies": { "@volar/typescript": "~2.4.11", "@vue/language-core": "2.2.2" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-1icPKkxAA5KTAaSwg0wVWdE48EdsH8fgvcbAiqojP4jXKl6LEM3soiW1aG/zrWrFt8Mw1ncG2vG1PvpZpVfehA=="],
"wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
@ -800,10 +875,16 @@
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"yoctocolors": ["yoctocolors@2.1.1", "", {}, "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ=="],
"zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
"zod-validation-error": ["zod-validation-error@3.4.0", "", { "peerDependencies": { "zod": "^3.18.0" } }, "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ=="],
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
@ -818,30 +899,40 @@
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
"@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"@nodelib/fs.scandir/@nodelib/fs.stat": ["@nodelib/fs.stat@4.0.0", "", {}, "sha512-ctr6bByzksKRCV0bavi8WoQevU6plSp2IkllIsEqaiKe2mwNNnaluhnRhcsgGZHrrHk57B3lf95MkLMO3STYcg=="],
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@vue/devtools-core/nanoid": ["nanoid@5.1.0", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-zDAl/llz8Ue/EblwSYwdxGBYfj46IM1dhjVi8dyp9LQffoIGxJEAHj2oeZ4uNcgycSRcQ83CnfcZqEJzVDLcDw=="],
"@vue/language-core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
"eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"eslint-plugin-vue/globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="],
"fast-glob/@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"vite/esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="],
"vite-plugin-inspect/@antfu/utils": ["@antfu/utils@0.7.10", "", {}, "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww=="],
"vue-eslint-parser/eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="],
"vue-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
@ -898,9 +989,7 @@
"@vue/language-core/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"eslint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"eslint/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"fast-glob/@nodelib/fs.walk/@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
@ -947,11 +1036,5 @@
"vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.24.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="],
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="],
"eslint/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"eslint/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"eslint/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
}
}

26
cfg/.prettierignore Executable file
View file

@ -0,0 +1,26 @@
# Tout ce qui est traité par dprint
*.css
*.html
*.js
*.json
*.jsonc
*.md
*.mjs
*.mts
*.ts
*.vue
*.yaml
*.yml
!package.json
# Cache
.cache
# Compilation
dist
# Jujutsu
.jj
# Zed
.zed

View file

@ -16,7 +16,7 @@
"jsonTrailingCommaFiles": [".swcrc", "biome.jsonc", "settings.json", "tsconfig.json"],
"lineWidth": 120,
"newLineKind": "lf",
"preferSingleLine": true,
"preferSingleLine": false,
"trailingCommas": "never",
"useTabs": false
},

View file

@ -6,19 +6,10 @@ import globals from "globals";
export default defineConfigWithVueTs(
{
files: ["**/*.{js,mjs,ts,mts,vue}"],
languageOptions: {
ecmaVersion: "latest",
globals: {
...globals.browser,
...globals.es2025,
},
},
languageOptions: { ecmaVersion: "latest", globals: { ...globals.browser, ...globals.es2025 } },
name: "app/files-to-lint",
},
{
ignores: [".cache/", "dist/", "node_modules/"],
name: "app/files-to-ignore",
},
{ ignores: [".cache/", "dist/", "node_modules/"], name: "app/files-to-ignore" },
vueTsConfigs.strictTypeChecked,
vueTsConfigs.stylisticTypeChecked,
vue.configs["flat/recommended"],
@ -29,9 +20,7 @@ export default defineConfigWithVueTs(
"vue/array-bracket-spacing": "off",
"vue/array-element-newline": "off",
"vue/arrow-spacing": "off",
"vue/attributes-order": ["error", {
alphabetical: true,
}],
"vue/attributes-order": ["error", { alphabetical: true }],
"vue/block-spacing": "off",
"vue/block-tag-newline": "off",
"vue/brace-style": "off",

13
cfg/knip.config.ts Normal file
View file

@ -0,0 +1,13 @@
import type { KnipConfig } from "knip";
const config: KnipConfig = {
entry: [
"vite.config.mts",
"src/main.ts",
"src/App.vue",
"pages/**/*.vue",
],
project: ["src/**/*.{ts,vue}"],
};
export default config;

View file

@ -10,14 +10,7 @@ const config = {
htmlWhitespaceSensitivity: "ignore",
jsxSingleQuote: false,
objectWrap: "collapse",
overrides: [
{
files: ["package.json"],
options: {
plugins: ["prettier-plugin-pkg"],
},
},
],
overrides: [{ files: ["package.json"], options: { plugins: ["prettier-plugin-pkg"] } }],
plugins: ["prettier-plugin-sh"],
printWidth: 120,
proseWrap: "never",

View file

@ -1 +1,17 @@
{ "words": ["oxlint", "lightningcss", "sqlocal", "Tmdb", "AUTOINCREMENT"] }
{
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
"version": "0.2",
"dictionaries": [
"fr-fr"
],
"words": [
"oxlint",
"lightningcss",
"sqlocal",
"Tmdb",
"AUTOINCREMENT",
"vtsls",
"quartary",
"fieldset"
]
}

1
env.d.ts vendored
View file

@ -1 +0,0 @@
/// <reference types="vite/client" />

View file

@ -1,13 +1,22 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>Journal Média</title>
<link rel="preload" href="/app-loading.css" as="style">
<link ref="preload" href="/fonts/Banquise-Regular.otf" as="font">
<link ref="preload" href="/fonts/BerkeleyMonoVariable-Regular" as="font">
<link rel="stylesheet" href="/app-loading.css">
</head>
<body>
<div id="app"></div>
<div id="app">
<div id="app-loading">
<p>loading</p>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

89
justfile Executable file
View file

@ -0,0 +1,89 @@
set shell := ["fish", "-c"]
# Variables de chemins de configuration.
drizzleConfigFile := "src/db/drizzle.config.ts"
prettierConfigFile := "cfg/prettier.config.mjs"
prettierIgnoreFile := "cfg/.prettierignore"
esLintConfigFile := "cfg/eslint.config.mts"
knipConfigFile := "cfg/knip.config.ts"
# Variables de cache.
cacheFolder := ".cache"
prettierCacheFile := "prettiercache"
stylelintCacheFile := "stylelintcache"
# Variables de dossiers.
stylesFolder := "src/styles/"
###
# Liste toutes les recettes.
list:
@just --list --list-heading 'Recettes disponibles :'\n'' --unsorted
# Lance le mode de développement.
dev:
bun --bun vite dev
# Compile le projet.
build:
-bun --bun vue-tsc --build .
bun --bun vite build
# Lance la prévisualisation du mode de production.
preview:
just build
bun --bun vite preview
# Met à jour les dépendances avec bun.
update:
bun update --latest
# Affiche les dépendances obsolètes avec bun.
outdated:
bun outdated --latest
# Génère un nouveau schéma de BDD.
db-generate-schema:
bun drizzle-kit --config {{ drizzleConfigFile }} generate
# Formate avec prettier puis dprint.
format:
bun --bun prettier \
--cache --cache-location "{{ cacheFolder }}/{{ prettierCacheFile }}" \
--config "{{ prettierConfigFile }}" \
--ignore-path ".gitignore" --ignore-path "{{ prettierIgnoreFile }}" \
--ignore-unknown \
--write \
.
dprint --config "cfg/dprint.json" fmt
watch-format:
watchexec dprint --config "cfg/dprint.json" fmt
# Vérifie le code CSS avec stylelint.
lint-css:
bun --bun stylelint \
--cache --cache-location "{{ cacheFolder }}/{{ stylelintCacheFile }}" \
--fix \
{{ stylesFolder }}
# Analyse le code TypeScript et Astro.
lint-js fix="":
bun --bun eslint --config "{{ esLintConfigFile }}" {{ fix }}
# Analyse l'orthographe et la grammaire.
lint-spelling:
bun --bun cspell lint *
# Vérifie la présence de « code mort ».
lint-dead-code:
bun knip-bun --config "{{ knipConfigFile }}"
# Vérifie tous les fichiers.
lint-all:
just lint-css
just lint-js

View file

@ -1,40 +1,49 @@
{
"name": "journal-media",
"version": "0.0.0",
"private": true,
"type": "module",
"private": true,
"scripts": {
"dev": "vite",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint:eslint": "eslint . --fix"
"dev": "vite",
"lint:eslint": "eslint . --fix",
"preview": "vite preview",
"type-check": "vue-tsc --build"
},
"dependencies": {
"@effect/platform": "^0.77.2",
"@effect/sql-drizzle": "^0.29.2",
"@unhead/vue": "^2.0.0-alpha.19",
"@vueuse/core": "^12.7.0",
"@vueuse/router": "^12.7.0",
"a11y-dialog": "^8.1.1",
"drizzle-orm": "^0.39.3",
"effect": "^3.13.2",
"pinia": "^3.0.1",
"sqlocal": "^0.14.0",
"unhead": "^2.0.0-alpha.19",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@types/bun": "^1.2.2",
"@types/node": "^22.13.4",
"@unhead/addons": "^2.0.0-alpha.19",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/eslint-config-typescript": "^14.4.0",
"@vue/typescript-plugin": "^2.2.2",
"browserslist": "^4.24.4",
"drizzle-kit": "^0.30.4",
"eslint": "^9.20.1",
"eslint": "^9.21.0",
"eslint-plugin-perfectionist": "^4.9.0",
"eslint-plugin-vue": "^9.32.0",
"globals": "^15.15.0",
"globals": "^16.0.0",
"jiti": "^2.4.2",
"knip": "^5.44.4",
"lightningcss": "^1.29.1",
"prettier": "^3.5.1",
"prettier-plugin-pkg": "^0.18.1",
"prettier-plugin-sh": "^0.15.0",
"tsr": "^1.3.4",
"typescript": "^5.7.3",
"vite": "^6.1.1",
"vite-plugin-vue-devtools": "^7.7.2",

40
public/app-loading.css Normal file
View file

@ -0,0 +1,40 @@
@import url("fonts.css");
#app-loading {
position: fixed;
inset: 0;
text-align: center;
align-content: center;
z-index: 100;
background: salmon;
font-family: Banquise;
font-size: 2rem;
p {
width: 10ch;
margin: auto;
}
p::after {
content: "";
animation: 2s forwards infinite loading;
}
}
@keyframes loading {
from {
content: "";
}
25% {
content: ".";
}
50% {
content: "..";
}
75% {
content: "...";
}
to {
content: "";
}
}

9
public/favicon/favicon.svg Executable file
View file

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

25
public/fonts.css Normal file
View file

@ -0,0 +1,25 @@
@font-face {
font-family: BRKLY;
font-weight: 100 150;
font-style: normal;
font-display: swap;
src: url("/fonts/BerkeleyMonoVariable-Regular.ttf") format("truetype");
}
@font-face {
font-family: BRKLY;
font-weight: 100 150;
font-style: italic;
font-display: swap;
src: url("/fonts/BerkeleyMonoVariable-Italic.ttf") format("truetype");
}
@font-face {
font-family: Banquise;
font-weight: 400;
font-style: normal;
font-display: swap;
src:
url("/fonts/Banquise-Regular.woff") format("woff"),
url("/fonts/Banquise-Regular.otf") format("opentype");
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,7 +1,33 @@
<script setup lang="ts">
import "@/styles/main.css";
import { Transition } from "vue";
import { RouterView } from "vue-router";
import SidebarView from "@/views/SidebarView.vue";
import MainHeader from "./components/MainHeader.vue";
</script>
<template>
<RouterView></RouterView>
<div class="container center with-sidebar">
<main class="box stack">
<MainHeader></MainHeader>
<RouterView v-slot="{ Component, route }">
<Transition name="fade" mode="out-in">
<component :is="Component" :key="route.path" />
</Transition>
</RouterView>
</main>
<SidebarView></SidebarView>
</div>
</template>
<style lang="css">
.fade-enter-active, .fade-leave-active {
transition: opacity 0.2s linear;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>

View file

@ -1,3 +0,0 @@
<template>
<p>Error</p>
</template>

View file

@ -0,0 +1,53 @@
<script setup lang="ts">
import A11yDialog from "a11y-dialog";
import { useTemplateRef } from "vue";
import { watchEffect } from "vue";
import { ref } from "vue";
import { computed } from "vue";
import type { ComputedRef } from "vue";
const props = defineProps<{
dialogId: string;
toggled: boolean;
}>();
defineEmits<{
(e: "dialog-hidden", dialogId: string): void;
}>();
const dialogTitleId: ComputedRef<string> = computed(() => `${props.dialogId}-title`);
const dialogContainer = useTemplateRef<HTMLDivElement>("dialog");
let dialog = ref<A11yDialog>();
const closeDialog = () => dialog.value?.hide();
watchEffect(() => {
if (dialogContainer.value) {
dialog.value = new A11yDialog(dialogContainer.value);
}
if (props.toggled) {
dialog.value?.show();
}
});
</script>
<template>
<div
aria-hidden="true" :aria-labelledby="dialogTitleId" class="dialog"
:id="dialogId" ref="dialog" @hide="$emit('dialog-hidden', dialogId)"
>
<div class="dialog-content box" role="document">
<header class="invert">
<h2 :id="dialogTitleId">
<slot name="title"></slot>
</h2>
<button @click="closeDialog" class="integrated" type="button">X</button>
</header>
<main class="box">
<slot name="content"></slot>
</main>
</div>
</div>
</template>
<style scoped src="@/styles/components/imposter-box.css"></style>

View file

@ -1,19 +1,25 @@
<script setup lang="ts">
import { type DiaryEntry } from "@/db/schemas/entries";
import { ReadApi } from "@/services/read-api";
import { Console, Effect, pipe } from "effect";
import { RuntimeClient } from "@/services/runtime-client";
import { Effect } from "effect";
const lastAddedEntry = await pipe(
Effect.andThen(ReadApi, (api: ReadApi) =>
pipe(
api.getLastAddedEntry(),
Effect.tapError(Console.warn),
const lastAddedEntry: DiaryEntry | null = await RuntimeClient.runPromise(
Effect.gen(function*() {
const readApi = yield* ReadApi;
return yield* readApi.getLastAddedEntry().pipe(
Effect.tapErrorCause(cause => Effect.logError(cause.toJSON())),
Effect.orElseSucceed(() => null),
)),
Effect.provide(ReadApi.Default),
Effect.runPromise,
);
}),
);
</script>
<template>
<p>hello</p>
<p v-if="lastAddedEntry">
{{ lastAddedEntry?.id }}
</p>
</template>

View file

@ -1,3 +0,0 @@
<template>
<p>loading...</p>
</template>

View file

@ -0,0 +1,8 @@
<script setup lang="ts">
</script>
<template>
<header>
<h1>Journal Média</h1>
</header>
</template>

View file

@ -0,0 +1,16 @@
<script setup lang="ts">
import { RouterLink } from "vue-router";
</script>
<template>
<aside class="box sidebar">
<nav id="primary-navigation">
<ul>
<li><RouterLink to="/">Accueil</RouterLink></li>
</ul>
</nav>
</aside>
</template>
<style scoped src="@/styles/components/navigation-menu.css">
</style>

View file

@ -1,20 +1,5 @@
import type { Config } from "drizzle-kit";
import { loadEnv } from "vite";
const env = loadEnv("development", process.cwd(), "");
const DATABASE_URL = env["DATABASE_URL"];
// L'URL de la BDD doit être valide.
if (!DATABASE_URL || DATABASE_URL === "") {
throw new Error("L'URL de la base de données doit être renseignée dans le fichier de variables d'environnement.");
}
const DrizzleKitConfig: Config = {
dbCredentials: { url: DATABASE_URL },
dialect: "sqlite",
out: "./src/db/drizzle",
schema: "./src/db/schema.ts",
};
const DrizzleKitConfig: Config = { dialect: "sqlite", out: "./src/db/drizzle", schema: "./src/db/schemas.ts" };
export default DrizzleKitConfig;

View file

@ -3,7 +3,7 @@ CREATE TABLE `art_works` (
`medium_type_id` integer,
`name` text NOT NULL,
`release_date` text(10) NOT NULL,
FOREIGN KEY (`medium_type_id`) REFERENCES `medium_types`(`id`) ON UPDATE no action ON DELETE no action
FOREIGN KEY (`medium_type_id`) REFERENCES `media_types`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `diary_entries` (
@ -40,14 +40,14 @@ CREATE TABLE `genres` (
--> statement-breakpoint
CREATE UNIQUE INDEX `genres_name_unique` ON `genres` (`name`);--> statement-breakpoint
CREATE UNIQUE INDEX `genres_slug_unique` ON `genres` (`slug`);--> statement-breakpoint
CREATE TABLE `medium_types` (
CREATE TABLE `media_types` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text NOT NULL,
`slug` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `medium_types_name_unique` ON `medium_types` (`name`);--> statement-breakpoint
CREATE UNIQUE INDEX `medium_types_slug_unique` ON `medium_types` (`slug`);--> statement-breakpoint
CREATE UNIQUE INDEX `media_types_name_unique` ON `media_types` (`name`);--> statement-breakpoint
CREATE UNIQUE INDEX `media_types_slug_unique` ON `media_types` (`slug`);--> statement-breakpoint
CREATE TABLE `users` (
`email` text NOT NULL,
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,

View file

@ -26,10 +26,10 @@
},
"indexes": {},
"foreignKeys": {
"art_works_medium_type_id_medium_types_id_fk": {
"name": "art_works_medium_type_id_medium_types_id_fk",
"art_works_medium_type_id_media_types_id_fk": {
"name": "art_works_medium_type_id_media_types_id_fk",
"tableFrom": "art_works",
"tableTo": "medium_types",
"tableTo": "media_types",
"columnsFrom": ["medium_type_id"],
"columnsTo": ["id"],
"onDelete": "no action",
@ -192,16 +192,16 @@
"uniqueConstraints": {},
"checkConstraints": {}
},
"medium_types": {
"name": "medium_types",
"media_types": {
"name": "media_types",
"columns": {
"id": { "name": "id", "type": "integer", "primaryKey": true, "notNull": true, "autoincrement": true },
"name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false },
"slug": { "name": "slug", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false }
},
"indexes": {
"medium_types_name_unique": { "name": "medium_types_name_unique", "columns": ["name"], "isUnique": true },
"medium_types_slug_unique": { "name": "medium_types_slug_unique", "columns": ["slug"], "isUnique": true }
"media_types_name_unique": { "name": "media_types_name_unique", "columns": ["name"], "isUnique": true },
"media_types_slug_unique": { "name": "media_types_slug_unique", "columns": ["slug"], "isUnique": true }
},
"foreignKeys": {},
"compositePrimaryKeys": {},

View file

@ -1,76 +0,0 @@
import type { Values } from "@/libs/utils/types";
import type { InferSelectModel } from "drizzle-orm";
import type { AnySQLiteColumn } from "drizzle-orm/sqlite-core";
import * as t from "drizzle-orm/sqlite-core";
import { sqliteTable as table } from "drizzle-orm/sqlite-core";
import type { DIARY_ENTRY_STATES, MEDIUM_TYPES } from "./constants";
/**
* Configuration de la BDD SQLite de l'application.
*
* Assumer ici que toute colonne :
* - non explicitement définie comme optionnelle est obligatoire ;
* - non explicitement définie comme unique peut contenir plusieurs fois la même valeur ;
*/
export const Users = table("users", {
email: t.text("email").notNull().unique(),
id: t.integer("id").primaryKey({ autoIncrement: true }),
name: t.text("name").notNull().unique(),
});
export type User = InferSelectModel<typeof Users>;
export const MediumTypes = table("medium_types", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
name: t.text("name").notNull().unique(),
slug: t.text("slug").$type<Values<typeof MEDIUM_TYPES>>().notNull().unique(),
});
export type MediumType = InferSelectModel<typeof MediumTypes>;
export const ArtWorks = table("art_works", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
mediumTypeId: t.integer("medium_type_id").references((): AnySQLiteColumn => MediumTypes.id),
name: t.text("name").notNull(),
releaseDate: t.text("release_date", { length: 10 }).notNull(),
});
export type ArtWork = InferSelectModel<typeof ArtWorks>;
export const Genres = table("genres", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
name: t.text("name").notNull().unique(),
slug: t.text("slug").notNull().unique(),
});
export type Genre = InferSelectModel<typeof Genres>;
export const DiaryEntries = table("diary_entries", {
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id),
dateCreated: t.text("date_created", { length: 10 }).notNull(),
dateModified: t.text("date_modified", { length: 10 }).notNull(),
id: t.integer("id").primaryKey({ autoIncrement: true }),
stateId: t.integer("state_id").references((): AnySQLiteColumn => DiaryEntriesStates.id),
userId: t.integer("user_id").references((): AnySQLiteColumn => Users.id),
});
export type DiaryEntry = InferSelectModel<typeof DiaryEntries>;
export const Viewings = table("viewings", {
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id),
date: t.text("date", { length: 10 }).notNull(),
id: t.integer("id").primaryKey({ autoIncrement: true }),
userId: t.integer("user_id").references((): AnySQLiteColumn => Users.id),
});
export type Viewing = InferSelectModel<typeof Viewings>;
export const DiaryEntriesGenres = table("diary_entries_genres", {
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id),
genreId: t.integer("genre_id").references((): AnySQLiteColumn => Genres.id),
id: t.integer("id").primaryKey({ autoIncrement: true }),
});
export type DiaryEntryGenre = InferSelectModel<typeof DiaryEntriesGenres>;
export const DiaryEntriesStates = table("diary_entries_states", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
state: t.text("state").$type<Values<typeof DIARY_ENTRY_STATES>>().notNull().unique(),
});
export type DiaryEntryState = InferSelectModel<typeof DiaryEntriesStates>;

View file

@ -1,4 +1,4 @@
export const MEDIUM_TYPES = {
export const MEDIA_TYPES = {
DOCUMENTARY: "documentary",
FILM: "film",
SERIES: "series",

40
src/db/schemas/entries.ts Normal file
View file

@ -0,0 +1,40 @@
import type { Values } from "@/libs/utils/types";
import type { InferSelectModel } from "drizzle-orm";
import type { AnySQLiteColumn } from "drizzle-orm/sqlite-core";
import * as t from "drizzle-orm/sqlite-core";
import { sqliteTable as table } from "drizzle-orm/sqlite-core";
import type { DIARY_ENTRY_STATES } from "./constants";
import { ArtWorks, Genres } from "./works";
export const DiaryEntries = table("diary_entries", {
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id),
dateCreated: t.text("date_created", { length: 10 }).notNull(),
dateModified: t.text("date_modified", { length: 10 }).notNull(),
id: t.integer("id").primaryKey({ autoIncrement: true }),
stateId: t.integer("state_id").references((): AnySQLiteColumn => DiaryEntriesStates.id).notNull(),
});
export const DiaryEntriesGenres = table("diary_entries_genres", {
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id).notNull(),
genreId: t.integer("genre_id").references((): AnySQLiteColumn => Genres.id),
id: t.integer("id").primaryKey({ autoIncrement: true }),
});
export const DiaryEntriesStates = table("diary_entries_states", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
state: t.text("state").$type<Values<typeof DIARY_ENTRY_STATES>>().notNull().unique(),
});
export const Viewings = table("viewings", {
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id).notNull(),
date: t.text("date", { length: 10 }).notNull(),
id: t.integer("id").primaryKey({ autoIncrement: true }),
});
export type DiaryEntry = InferSelectModel<typeof DiaryEntries>;
export type DiaryEntryGenre = InferSelectModel<typeof DiaryEntriesGenres>;
export type DiaryEntryState = InferSelectModel<typeof DiaryEntriesStates>;
export type Viewing = InferSelectModel<typeof Viewings>;

31
src/db/schemas/works.ts Normal file
View file

@ -0,0 +1,31 @@
import type { Values } from "@/libs/utils/types";
import type { InferSelectModel } from "drizzle-orm";
import type { AnySQLiteColumn } from "drizzle-orm/sqlite-core";
import * as t from "drizzle-orm/sqlite-core";
import { sqliteTable as table } from "drizzle-orm/sqlite-core";
import type { MEDIA_TYPES } from "./constants";
export const MediaTypes = table("media_types", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
name: t.text("name").notNull().unique(),
slug: t.text("slug").$type<Values<typeof MEDIA_TYPES>>().notNull().unique(),
});
export const ArtWorks = table("art_works", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
mediumTypeId: t.integer("medium_type_id").references((): AnySQLiteColumn => MediaTypes.id).notNull(),
name: t.text("name").notNull(),
releaseDate: t.text("release_date", { length: 10 }).notNull(),
});
export const Genres = table("genres", {
id: t.integer("id").primaryKey({ autoIncrement: true }),
name: t.text("name").notNull().unique(),
slug: t.text("slug").notNull().unique(),
});
export type ArtWork = InferSelectModel<typeof ArtWorks>;
export type Genre = InferSelectModel<typeof Genres>;
export type MediaType = InferSelectModel<typeof MediaTypes>;

View file

@ -15,28 +15,33 @@ export class TmdbMovieSearchQueryParams extends Schema.Class<TmdbMovieSearchQuer
),
primary_release_year: Schema.NonEmptyString.pipe(Schema.length(4), Schema.optional),
query: Schema.NonEmptyString,
region: Schema.NonEmptyString.pipe(Schema.propertySignature, Schema.withConstructorDefault(() => "fr")),
region: Schema.NonEmptyString.pipe(
Schema.propertySignature,
Schema.withConstructorDefault(() => "fr"),
),
year: Schema.NonEmptyString.pipe(Schema.length(4), Schema.optional),
}) {}
export class TmdbMovieSearchResponse extends Schema.Class<TmdbMovieSearchResponse>("TmdbMovieSearchResponse")({
page: Schema.NonNegativeInt,
results: Schema.Array(Schema.Struct({
adult: Schema.Boolean,
backdrop_path: Schema.Union(Schema.String, Schema.Null),
genre_ids: Schema.Array(Schema.NonNegativeInt),
id: Schema.NonNegativeInt,
original_language: Schema.String,
original_title: Schema.String,
overview: Schema.String,
popularity: Schema.Number,
poster_path: Schema.Union(Schema.String, Schema.Null),
release_date: Schema.NonEmptyString.pipe(Schema.length(10)),
title: Schema.String,
video: Schema.Boolean,
vote_average: Schema.Number,
vote_count: Schema.NonNegativeInt,
})),
results: Schema.Array(
Schema.Struct({
adult: Schema.Boolean,
backdrop_path: Schema.Union(Schema.String, Schema.Null),
genre_ids: Schema.Array(Schema.NonNegativeInt),
id: Schema.NonNegativeInt,
original_language: Schema.String,
original_title: Schema.String,
overview: Schema.String,
popularity: Schema.Number,
poster_path: Schema.Union(Schema.String, Schema.Null),
release_date: Schema.NonEmptyString.pipe(Schema.length(10)),
title: Schema.String,
video: Schema.Boolean,
vote_average: Schema.Number,
vote_count: Schema.NonNegativeInt,
}),
),
total_pages: Schema.NonNegativeInt,
total_results: Schema.NonNegativeInt,
}) {}

16
src/libs/types/events.ts Normal file
View file

@ -0,0 +1,16 @@
/** Événement émis au clic d'un Élément demandant l'ouverture d'une modale particulière. */
export class DialogWantedEvent extends Event {
/** L'ID de la modale demandée. */
dialogId: string;
constructor(dialogId: string) {
super("dialog-wanted", { bubbles: true, composed: true });
this.dialogId = dialogId;
}
}
declare global {
interface GlobalEventHandlersEventMap {
"dialog-wanted": DialogWantedEvent;
}
}

7
src/libs/utils/dates.ts Normal file
View file

@ -0,0 +1,7 @@
export const getTodayDate = (): string =>
new Date(Date.now()).toLocaleDateString("fr-FR", {
day: "numeric",
month: "long",
weekday: "long",
year: "numeric",
});

69
src/pages/HomePage.vue Normal file
View file

@ -0,0 +1,69 @@
<script setup lang="ts">
import ImposterBox from "@/components/ImposterBox.vue";
import type { Ref } from "vue";
import { onMounted, ref } from "vue";
const toggleDialogStateRef = (stateRef: Ref<boolean, boolean>) => () => {
stateRef.value = !stateRef.value;
};
const isAddMediaToggled = ref(false);
const toggleAddMediaDialog = toggleDialogStateRef(isAddMediaToggled);
onMounted(() => {
console.debug("HomePage.vue -- Mounted");
});
</script>
<template>
<section class="cluster" id="buttons">
<button @click="toggleAddMediaDialog" id="add-media-button" type="button">🬤 Ajouter un média</button>
<button id="" type="button">🬗 Rechercher une entrée</button>
</section>
<section class="stack" id="last-watched-media">
<h2>Derniers médias regardés</h2>
</section>
<ImposterBox @dialog-hidden="toggleAddMediaDialog" :toggled="isAddMediaToggled" dialog-id="add-media">
<template v-slot:title>Ajouter un média</template>
<template v-slot:content>
<form class="stack">
<fieldset class="cluster">
<legend>Type du média</legend>
<div class="field">
<input
id="film" checked name="media-type"
type="radio" value="film"
>
<label for="film">Film</label>
</div>
<div class="field">
<input
id="series" name="media-type" type="radio"
value="series"
>
<label for="series">Série</label>
</div>
</fieldset>
<div class="field stack">
<label for="media-title">Titre</label> <input id="media-title" type="text">
</div>
<div class="field stack">
<label for="media-release-year">Année de sortie</label> <input id="media-release-year" type="number">
</div>
<div class="cluster buttons">
<button class="invert" type="submit">
Rechercher
</button>
<button type="reset">
Réinitialiser
</button>
</div>
</form>
</template>
</ImposterBox>
</template>

View file

@ -0,0 +1,13 @@
<script setup lang="ts">
import { onMounted } from "vue";
// DEBUG
onMounted(() => {
console.debug("NotFoundPage.vue -- Mounted");
});
</script>
<template>
<section class="stack" id="404">
<h2>404</h2>
</section>
</template>

View file

@ -1,15 +1,40 @@
import HomeView from "@/views/HomeView.vue";
import { createRouter, createWebHistory } from "vue-router";
import HomePage from "@/pages/HomePage.vue";
import { Option, pipe, Predicate } from "effect";
import { createRouter, createWebHistory, type Router } from "vue-router";
const router = createRouter({
const siteName = "Journal Média";
const separator = "|";
const generatePageTitle = (siteName: string, separator: string, pageTitle: string): string =>
[siteName, separator, pageTitle].join(" ");
const router: Router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
component: HomeView,
name: "home",
component: HomePage,
meta: { title: "Accueil" },
name: "Home",
path: "/",
},
{
component: () => import("@/pages/NotFoundPage.vue"),
meta: { title: "404" },
name: "NotFound",
path: "/:pathMatch(.*)*",
},
],
});
router.beforeEach((to, _): void => {
console.debug("router - to", to);
pipe(
Option.liftPredicate(Predicate.isString)(to.meta["title"]),
Option.getOrElse((): string => "???"),
(pageName: string): void => {
document.title = generatePageTitle(siteName, separator, pageName);
},
);
});
export default router;

25
src/router/typed-routes.d.ts vendored Normal file
View file

@ -0,0 +1,25 @@
import type { RouteRecordInfo } from "vue-router";
export interface RouteNamedMap {
Home: RouteRecordInfo<
"Home",
"/",
Record<never, never>,
Record<never, never>,
{ title: string }
>;
NotFound: RouteRecordInfo<
"NotFound",
"/:pathMatch(.*)*",
{ path: string },
{ path: string },
{ title: string }
>;
}
// Last, you will need to augment the Vue Router types with this map of routes
declare module "vue-router" {
interface TypesConfig {
RouteNamedMap: RouteNamedMap;
}
}

View file

@ -1,9 +1,8 @@
import { drizzle } from "drizzle-orm/sqlite-proxy";
import { drizzle, type SqliteRemoteDatabase } from "drizzle-orm/sqlite-proxy";
import { Data, Effect } from "effect";
import { SQLocalDrizzle } from "sqlocal/drizzle";
class LocalSqliteError extends Data.TaggedError("LocalSqliteError")<{ cause: unknown }> {
}
class LocalSqliteError extends Data.TaggedError("LocalSqliteError")<{ cause: unknown }> {}
export class LocalSqlite extends Effect.Service<LocalSqlite>()("LocalSqlite", {
effect: Effect.gen(function*() {
@ -30,13 +29,13 @@ export class LocalSqlite extends Effect.Service<LocalSqlite>()("LocalSqlite", {
}),
});
const orm = drizzle(client.driver, client.batchDriver);
const orm: SqliteRemoteDatabase = drizzle(client.driver, client.batchDriver);
const query = <R>(execute: (_: typeof orm) => Promise<R>) =>
Effect.tryPromise({
catch: (error: unknown) => new LocalSqliteError({ cause: error }),
try: () => execute(orm),
});
Effect.tryPromise({ catch: (error: unknown) => new LocalSqliteError({ cause: error }), try: () => execute(orm) });
yield* Effect.logDebug("--- DB ---");
yield* Effect.tryPromise(() => client.deleteDatabaseFile());
return { client, orm, query };
}),

14
src/services/logger.ts Normal file
View file

@ -0,0 +1,14 @@
import type { LogLevel } from "effect/LogLevel";
import { Config, ConfigProvider, Effect, Layer, Logger, pipe } from "effect";
const EnvConfigProvider = Layer.setConfigProvider(ConfigProvider.fromMap(new Map([["LOG_LEVEL", "DEBUG"]])));
const LogLevelLive = pipe(
Config.logLevel("LOG_LEVEL"),
Effect.andThen((level: LogLevel) => Logger.minimumLogLevel(level)),
Layer.unwrapEffect, // Convertis l'Effect en Layer
Layer.provide(EnvConfigProvider),
);
export const PrettyLogger = Layer.mergeAll(Logger.pretty, LogLevelLive);

View file

@ -0,0 +1,20 @@
import type { SQLocalDrizzle } from "sqlocal/drizzle";
import v0000 from "@/db/drizzle/0000_perfect_justice.sql?raw";
import { Data, Effect } from "effect";
import { LocalSqlite } from "./db";
class MigrationsError extends Data.TaggedError("MigrationsError")<{ cause: unknown }> {}
const executeRawSql = (client: SQLocalDrizzle) => (sql: string) =>
Effect.tryPromise({ catch: (error: unknown) => new MigrationsError({ cause: error }), try: () => client.sql(sql) });
export class Migrations extends Effect.Service<Migrations>()("Migrations", {
dependencies: [LocalSqlite.Default],
effect: Effect.gen(function*() {
const db = yield* LocalSqlite;
yield* Effect.logDebug("--- MIGRATIONS ---");
return [yield* executeRawSql(db.client)(v0000)] as const;
}),
}) {}

View file

@ -1,4 +1,4 @@
import { DiaryEntries } from "@/db/schemas";
import { DiaryEntries, Users } from "@/db/schemas";
import { singleResultOrFail } from "@/libs/utils/effects";
import { desc } from "drizzle-orm";
import { Data, Effect } from "effect";
@ -12,11 +12,15 @@ export class ReadApi extends Effect.Service<ReadApi>()("ReadApi", {
effect: Effect.gen(function*() {
const { query } = yield* LocalSqlite;
yield* Effect.logDebug("--- READ-API ---");
return {
getAllEntries: () => query(_ => _.select().from(DiaryEntries)),
getLastAddedEntry: () =>
query(_ => _.select().from(DiaryEntries).limit(1).orderBy(desc(DiaryEntries.dateCreated))).pipe(
singleResultOrFail(() => new ReadApiError({ cause: "Aucune entrée n'a encore été ajoutée." })),
),
getUsers: () => query(_ => _.select().from(Users)),
};
}),
}) {}

View file

@ -0,0 +1,15 @@
import { Layer, ManagedRuntime } from "effect";
import { LocalSqlite } from "./db";
import { PrettyLogger } from "./logger";
import { Migrations } from "./migrations";
import { ReadApi } from "./read-api";
const MainLayer = Layer.mergeAll(
// WriteApi.Default,
LocalSqlite.Default,
Migrations.Default,
ReadApi.Default,
).pipe(Layer.provide(PrettyLogger));
export const RuntimeClient = ManagedRuntime.make(MainLayer);

119
src/styles/base/base.css Executable file
View file

@ -0,0 +1,119 @@
/*
* 1. Utilise un meilleur modèle de boîte.
* 2. Fait que seul font-size puisse influencer la taille du texte.
* 3. Applique les schémas de couleurs.
* 4. Utilise une indentation plus étroite.
* 5. Permet l'usage de propriétés intrinsèques comme auto ou fit-content dans les animations.
*/
html {
box-sizing: border-box; /* 1 */
tab-size: 2; /* 4 */
color-scheme: dark light; /* 3 */
interpolate-size: allow-keywords; /* 6 */
/* stylelint-disable */
-moz-text-size-adjust: none; /* 2 */
-webkit-text-size-adjust: none; /* 2 */
text-size-adjust: none; /* 2 */
block-size: 100%;
/* stylelint-enable */
/* scrollbar-gutter: stable; */
}
body {
accent-color: var(--color-tertiary);
background: var(--color-secondary);
--webkit-font-smoothing: antialiased;
font-family: monospace, system-ui, sans-serif;
font-optical-sizing: auto;
font-kerning: normal;
font-variant-ligatures: common-ligatures no-discretionary-ligatures no-historical-ligatures
contextual;
line-height: var(--line-height-comfortable);
text-decoration-skip-ink: auto;
text-rendering: geometricprecision;
}
/*
* 1. Hérite le modèle de boîte de l'élément racine.
* 2. Désactive toute marge pour partir de bases saines.
* 3. Hérite toute propriété typographique et de couleur pour éviter des redéfinitions.
*/
*, *::before, *::after {
box-sizing: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 2 */
font: inherit; /* 3 */
font-feature-settings: inherit; /* 3 */
font-variation-settings: inherit; /* 3 */
color: inherit; /* 3 */
letter-spacing: inherit; /* 3 */
word-spacing: inherit; /* 3 */
}
/* Utilise une couleur particulière pour l'arrière-plan des éléments sélectionnés avec le curseur. */
*::selection {
color: var(--color-secondary);
background: var(--color-primary);
}
/* TODO: Prendre en compte a11y-dialog */
/* Empêche le défilement de la page quand une modale est ouverte. */
:where(html:has(dialog:modal[open])) {
overflow: clip;
}
/* Retire les bordures et applique un modèle d'arrière-plan plus sain. */
*:where(:not(progress, meter)) {
background-repeat: no-repeat;
background-origin: border-box;
border: 0 solid transparent;
}
/* Classe pour cacher visuellement tout en restant accessible par les lecteurs d'écran. */
:where(.visually-hidden:not(:focus, :active, :focus-within)) {
position: absolute;
overflow: hidden;
inline-size: 1px;
block-size: 1px;
white-space: nowrap;
clip-path: inset(50%);
}
/* Cache les éléments cachés. */
:where([hidden]) {
display: none;
}
/* Affiche un curseur « Désactivé » pour les éléments désactivés. */
:where([disabled]) {
cursor: not-allowed;
}
/* Applique des contours de focus visibles. */
:where(:focus-visible) {
outline: currentcolor solid 0.2rem;
outline-offset: 0.2rem;
}
:where(:focus-visible, :target) {
scroll-margin-block: 8vh;
}
/* Active une transition de page simple. */
@view-transition {
navigation: auto;
}
/* Désactive animations et transitions pour les Utilisateurs le demandant explicitement. */
@media (prefers-reduced-motion) {
*, *::before, *::after {
scroll-behavior: auto !important;
transition: none !important;
animation-duration: 0s !important;
}
@view-transition {
navigation: none !important;
}
}

View file

@ -0,0 +1,56 @@
/* Réinitialise l'apparence d'éléments interactifs. */
:where(button, fieldset, input, select, textarea) {
appearance: none;
}
/* Hauteur de ligne plus étroite pour les éléments interactifs. */
:where(button, fieldset, input, label, select, textarea) {
line-height: var(--line-height-compact);
}
:where(textarea) {
resize: vertical;
}
@supports (resize: block) {
:where(textarea) {
resize: block;
}
}
/* Curseur de main pour les éléments interactifs cliquables. */
:where(button, label, select) {
cursor: pointer;
}
:where(button) {
inline-size: fit-content;
}
/* Évite le dépassement des textes. */
:where(p, h1, h2, h3, h4, h5, h6) {
overflow-wrap: break-word;
}
/* Les médias doivent occuper toute la longueur disponible au sein de leur propre bloc. */
:where(img, picture, video, canvas, svg) {
display: block;
max-inline-size: 100%;
block-size: auto;
}
:where(ol, ul) {
list-style: none;
}
:where(a) {
text-decoration: none;
}
/*
* Empêche les marqueurs de listes de modifier la hauteur de ligne sur Firefox.
* https://danburzo.ro/notes/moz-bullet-font
*/
::marker {
line-height: 0;
}

39
src/styles/base/variables.css Executable file
View file

@ -0,0 +1,39 @@
:root {
/* Couleurs */
/* Réelles */
--color-white: #fff;
--color-black: #000;
/* Logiques */
--color-primary: black;
--color-secondary: salmon;
--color-tertiary: dimgrey;
--color-quartary: #be2727;
/* Typographie */
/* Hauteurs de ligne */
--line-height-comfortable: 1.4;
--line-height-compact: 1.1;
/* Échelles */
--ratio: 1.4;
--s-5: calc(var(--s-4) / var(--ratio));
--s-4: calc(var(--s-3) / var(--ratio));
--s-3: calc(var(--s-2) / var(--ratio));
--s-2: calc(var(--s-1) / var(--ratio));
--s-1: calc(var(--s0) / var(--ratio));
--s0: 1rem;
--s1: calc(var(--s0) * var(--ratio));
--s2: calc(var(--s1) * var(--ratio));
--s3: calc(var(--s2) * var(--ratio));
--s4: calc(var(--s3) * var(--ratio));
--s5: calc(var(--s4) * var(--ratio));
/* Arrière-plans à motifs */
--bg25-secondary: repeating-conic-gradient(var(--color-tertiary) 0% 25%, transparent 0% 100%) 1px
0.5px / 2px 2px;
--bg75-secondary: repeating-conic-gradient(var(--color-secondary) 0% 75%, transparent 0% 100%) 1px
0.5px / 2px 2px;
}

View file

@ -0,0 +1,55 @@
.dialog {
position: fixed; /* 1 */
z-index: 2; /* 1 */
inset: 0; /* 1 */
display: flex; /* 2 */
background: var(--bg25-secondary);
margin: 0;
&[aria-hidden="true"] {
display: none; /* 1 */
}
}
.dialog-content {
position: relative; /* 2 */
max-inline-size: 80ch;
margin: auto; /* 1 */
padding: initial;
border: 1px solid var(--color-primary);
background-color: var(--color-secondary);
box-shadow: 0.5rem 0.5rem 0 0 var(--color-primary);
animation: fade-in 100ms 10ms both;
header {
overflow: hidden;
display: flex;
flex-flow: row nowrap;
align-items: center;
justify-content: space-between;
border-block-end: 1px solid var(--color-primary);
h2 {
padding-inline: var(--s-1);
font-size: var(--s1);
}
button {
padding: var(--s-1);
font-family: Banquise, monospace;
font-weight: 500;
}
}
}
@keyframes fade-in {
from {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.dialog-overlay, .dialog-content {
animation: none;
}
}

View file

@ -0,0 +1,3 @@
[aria-current] {
font-weight: 125;
}

View file

@ -0,0 +1,7 @@
@import url("./box.css");
@import url("./center.css");
@import url("./cover.css");
@import url("./cluster.css");
@import url("./sidebar.css");
@import url("./stack.css");
@import url("./switcher.css");

17
src/styles/layouts/box.css Executable file
View file

@ -0,0 +1,17 @@
/*
* 1. Affiche une bordure pour les thèmes à haut contraste sans augmenter la taille de la boîte.
*/
.box {
padding: var(--s0, 1rem);
background-color: var(--color-secondary, #fff);
outline: 0.125rem solid transparent; /* 1 */
outline-offset: -0.125rem; /* 1 */
/* border: 1px solid var(--color-primary); */
}
/* Inverse les couleurs de la boîte. */
.box.invert {
color: var(--color-secondary, #fff);
background-color: var(--color-primary, #000);
}

14
src/styles/layouts/center.css Executable file
View file

@ -0,0 +1,14 @@
/*
* 1. Utilise un modèle de boîte excluant les marges internes du calcul de la longueur.
* 2. Centre l'Élément avec des marges externes automatiques en ligne et une longueur maximale.
* 3. Assure que des espaces latéraux sont présents.
*/
.center {
display: flex;
flex-flow: column nowrap;
align-items: center;
box-sizing: content-box; /* 1 */
max-inline-size: var(--longueur-max-texte, 80ch); /* 2 */
margin-inline: auto; /* 2 */
padding-inline: var(--s0, 1rem) var(--s0, 1rem); /* 3 */
}

View file

@ -0,0 +1,8 @@
.cluster {
display: flex;
flex-flow: row wrap;
gap: var(--s0, 1rem);
align-items: center;
/* justify-content: center; */
}

40
src/styles/layouts/cover.css Executable file
View file

@ -0,0 +1,40 @@
/*
* Cover est une disposition permettant de centrer verticalement en son sein un Élément, avec
* potentiellement une en-tête et/ou potentiellement un pied de page à chaque extrémité.
*
* Un usage typique de Cover est l'affichage d'une partie introductive au sein d'une page.
*
* 1. Arrange les Éléments en colonne.
* 2. Fait que la disposition ait une taille minimale fixe tout en permettant son expansion si le contenu la dépasse.
* 3. Empêche l'Élément centrale de la disposition de toucher les bords.
*/
.cover {
--cover-minimal-height: 100vb;
--cover-container-padding: var(--s0, 1rem);
--cover-inter-element-spacing: var(--s0, 1rem);
display: flex; /* 1 */
flex-flow: column nowrap; /* 1 */
min-block-size: var(--cover-minimal-height); /* 2 */
padding: var(--cover-container-padding); /* 3 */
/* Applique un espacement inter-élément pour tout Élément supplémentaire. */
> * {
margin-block: var(--cover-inter-element-spacing);
}
/* Pas de marge pour les Éléments aux extrémités de la disposition. */
> :first-child:not(.cover-center) {
margin-block-start: 0;
}
/* Pas de marge pour les Éléments aux extrémités de la disposition. */
> :last-child:not(.cover-center) {
margin-block-end: 0;
}
/* Centre verticalement l'Élément central. */
> .cover-center {
margin-block: auto;
}
}

View file

@ -0,0 +1,19 @@
.with-sidebar {
--gutter: var(--s0);
display: flex;
flex-flow: row wrap;
gap: var(--gutter, var(--s0));
align-content: start;
}
.with-sidebar > :first-child {
flex-basis: 0;
flex-grow: 999;
min-inline-size: 65%;
}
.with-sidebar > :last-child {
flex-basis: 15ch;
flex-grow: 1;
}

19
src/styles/layouts/stack.css Executable file
View file

@ -0,0 +1,19 @@
.stack {
--space: var(--s0, 1rem);
display: flex;
flex-flow: column nowrap;
justify-content: flex-start;
}
.stack > * {
margin-block: 0;
}
.stack > * + * {
margin-block-start: var(--space);
}
.stack:only-child {
block-size: 100%;
}

View file

@ -0,0 +1,16 @@
.switcher {
--threshold: 30rem;
display: flex;
flex-flow: row wrap;
gap: var(--s0, 1rem);
}
.switcher > *:not(.column-separator) {
flex-basis: calc((var(--threshold) - 100%) * 999);
flex-grow: 1;
}
.switcher > :nth-last-child(n+5), .switcher > :nth-last-child(n+5) ~ * {
flex-basis: 100%;
}

9
src/styles/main.css Executable file
View file

@ -0,0 +1,9 @@
@layer base, elements, layouts, components, themes;
@import url("./base/variables.css");
@import url("./base/base.css") layer(base);
@import url("./base/elements.css") layer(base);
@import url("./layouts/_layouts.css") layer(layouts);
@import url("./themes/default.css") layer(themes);

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,60 @@
@import url("./default/buttons.css");
@import url("./default/headings.css");
body {
font-family: BRKLY, sans-serif;
font-weight: 100;
background: var(--color-secondary);
}
*:focus-visible {
animation: flicker 50ms 2;
}
.invert {
font-weight: 120;
color: var(--color-secondary);
background-color: var(--color-primary);
}
h1 {
font-family: Banquise, monospace;
}
.container {
--longueur-max-texte: 100ch;
place-content: start;
place-items: start;
/* opacity: 0;
animation: flicker 50ms 4 forwards 100ms ease-in; */
}
.container > :is(main, aside) {
margin-block-start: var(--s2);
}
main > header {
margin-block-end: var(--s2);
}
#last-watched-media {
--space: var(--s2);
}
@keyframes fade-in {
to {
opacity: 1;
}
}
@keyframes flicker {
from, 49% {
opacity: 0;
}
50%, to {
opacity: 1;
}
}

View file

@ -0,0 +1,54 @@
button {
--button-background-color: var(--color-secondary);
--button-border-color: var(--color-primary);
--button-font-weight: 100;
--button-padding: var(--s-1);
--button-text-color: var(--color-primary);
padding: var(--button-padding);
border: 1px solid var(--button-border-color);
font-weight: var(--button-font-weight);
color: var(--button-text-color);
background-color: var(--button-background-color);
box-shadow: 4px 4px 0 0 var(--color-primary);
&:hover {
--button-background-color: var(--color-primary);
--button-border-color: var(--color-secondary);
--button-font-weight: 120;
--button-text-color: var(--color-secondary);
}
&:active {
transform: translateX(2px) translateY(2px);
box-shadow: 1px 1px 0 0 var(--color-primary);
}
&:focus-visible {
outline-offset: -0.3rem;
}
/* Inversion des couleurs. */
&.invert {
--button-background-color: var(--color-primary);
--button-border-color: var(--color-secondary);
--button-text-color: var(--color-secondary);
outline-color: var(--color-secondary);
}
/* TODO: Déplacer dans un Composant. */
/* Bouton intégré dans un ensemble. */
&.integrated {
border: initial;
&:hover {
color: var(--color-primary);
background: var(--bg75-secondary);
}
&:active {
transform: translateX(2px) translateY(2px);
}
}
}

View file

@ -0,0 +1,12 @@
h1 {
font-family: Banquise, monospace;
font-size: var(--s3);
font-weight: 600;
letter-spacing: 1.5px;
}
h2 {
font-family: Banquise, monospace;
font-size: var(--s2);
letter-spacing: 1px;
}

4
src/views/BaseView.vue Normal file
View file

@ -0,0 +1,4 @@
<script setup lang="ts">
</script>
<template></template>

View file

@ -1,20 +0,0 @@
<script setup lang="ts">
import { defineAsyncComponent, Suspense } from "vue";
const AsyncComp = defineAsyncComponent({
delay: 0,
loader: () => import("@/components/LastAddedEntry.vue"),
timeout: 3000,
});
</script>
<template>
<main>
<Suspense>
<AsyncComp></AsyncComp>
<template #fallback>
Loading...
</template>
</Suspense>
</main>
</template>

View file

@ -0,0 +1,7 @@
<script setup lang="ts">
import NavigationMenu from "@/components/NavigationMenu.vue";
</script>
<template>
<NavigationMenu></NavigationMenu>
</template>

22
src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1,22 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
/** Le nom du fichier contenant la base de données SQLite. */
readonly VITE_DATABASE_NAME: string;
/** La clé API de l'application liée à un compte TMDB. */
readonly VITE_TMDB_API_KEY: string;
}
declare module "bun" {
interface Env {
/** Le nom du fichier contenant la base de données SQLite. */
readonly VITE_DATABASE_NAME: string;
/** La clé API de l'application liée à un compte TMDB. */
readonly VITE_TMDB_API_KEY: string;
}
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
export {};

View file

@ -16,7 +16,7 @@
"isolatedModules": true,
"jsx": "preserve",
"jsxImportSource": "vue",
"lib": ["ESNext", "DOM", "DOM.Iterable", "DOM.AsyncIterable"],
"lib": ["ESNext", "DOM", "DOM.Iterable", "DOM.AsyncIterable", "WebWorker"],
"module": "ESNext",
"moduleDetection": "force",
"moduleResolution": "bundler",
@ -32,11 +32,12 @@
"noUncheckedSideEffectImports": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true,
"paths": { "@/*": ["./src/*"] },
"plugins": [{ "name": "@vue/typescript-plugin" }],
"resolveJsonModule": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"strict": true,
"skipDefaultLibCheck": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
@ -46,6 +47,6 @@
"useUnknownInCatchVariables": true,
"verbatimModuleSyntax": true
},
"exclude": ["dist/", ".cache"],
"include": ["env.d.ts", "**/*", "**/*.vue"]
"exclude": ["node_modules", "dist/", ".cache"],
"include": ["src/router/typed-routes.d.ts", "src/env.d.ts", "**/*", "**/*.vue"]
}

View file

@ -1,3 +1,4 @@
import unheadVite from "@unhead/addons/vite";
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import vueDevTools from "vite-plugin-vue-devtools";
@ -8,33 +9,37 @@ export default defineConfig({
emptyOutDir: true,
outDir: "dist",
reportCompressedSize: true,
rollupOptions: {
output: {
compact: true,
format: "esm",
validate: true,
},
},
rollupOptions: { output: { compact: true, format: "esm", validate: true } },
sourcemap: false,
},
cacheDir: ".cache/vite",
clearScreen: false,
css: {
transformer: "lightningcss",
},
optimizeDeps: {
exclude: ["sqlocal"],
},
css: { transformer: "lightningcss" },
optimizeDeps: { exclude: ["sqlocal"] },
plugins: [
vue(),
vueDevTools(),
// vueDevTools(),
{
configureServer: server => {
server.middlewares.use((_req, res, next) => {
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.setHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.setHeader("Access-Control-Allow-Origin", "http://localhost:4321");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
next();
});
},
name: "configure-response-headers",
},
unheadVite(),
],
resolve: {
alias: {
"@": Bun.fileURLToPath(new URL("./src", import.meta.url)),
},
},
worker: {
format: "es",
},
worker: { format: "es" },
});