From 4016a8eff04e5b38d2d9dab11d0b680a6c11a1a5 Mon Sep 17 00:00:00 2001 From: ItsMii176 Date: Thu, 26 Jun 2025 15:44:18 +0200 Subject: [PATCH 1/7] =?UTF-8?q?=E2=9E=95=20add=20TypeScript=20seeding=20sc?= =?UTF-8?q?ript=20for=20Supabase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 2 - package.json | 5 + pnpm-lock.yaml | 947 ++++++++++++++++++++++++++++++++++++++++++++++ src/tools/seed.ts | 104 +++++ 4 files changed, 1056 insertions(+), 2 deletions(-) delete mode 100644 .env.example create mode 100644 src/tools/seed.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index 810d7e2..0000000 --- a/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co -NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here \ No newline at end of file diff --git a/package.json b/package.json index f300d5a..29ea283 100644 --- a/package.json +++ b/package.json @@ -9,16 +9,20 @@ "start": "next start" }, "dependencies": { + "@faker-js/faker": "^9.8.0", "@hookform/resolvers": "^5.1.1", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", + "@solana/wallet-standard-features": "^1.3.0", "@supabase/ssr": "^0.6.1", "@supabase/supabase-js": "^2.50.0", "@t3-oss/env-nextjs": "^0.13.8", + "biome": "^0.3.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "dotenv": "^16.5.0", "lucide-react": "^0.514.0", "next": "15.3.3", "next-safe-action": "^8.0.3", @@ -39,6 +43,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4", + "ts-node": "^10.9.2", "tw-animate-css": "^1.3.4", "typescript": "^5" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f8b88b5..fe0247c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@faker-js/faker': + specifier: ^9.8.0 + version: 9.8.0 '@hookform/resolvers': specifier: ^5.1.1 version: 5.1.1(react-hook-form@7.58.1(react@19.1.0)) @@ -23,6 +26,9 @@ importers: '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@solana/wallet-standard-features': + specifier: ^1.3.0 + version: 1.3.0 '@supabase/ssr': specifier: ^0.6.1 version: 0.6.1(@supabase/supabase-js@2.50.0) @@ -32,12 +38,18 @@ importers: '@t3-oss/env-nextjs': specifier: ^0.13.8 version: 0.13.8(typescript@5.8.3)(zod@3.25.67) + biome: + specifier: ^0.3.3 + version: 0.3.3 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 clsx: specifier: ^2.1.1 version: 2.1.1 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 lucide-react: specifier: ^0.514.0 version: 0.514.0(react@19.1.0) @@ -93,6 +105,9 @@ importers: tailwindcss: specifier: ^4 version: 4.1.10 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.19.0)(typescript@5.8.3) tw-animate-css: specifier: ^1.3.4 version: 1.3.4 @@ -163,9 +178,17 @@ packages: cpu: [x64] os: [win32] + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@emnapi/runtime@1.4.3': resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + '@faker-js/faker@9.8.0': + resolution: {integrity: sha512-U9wpuSrJC93jZBxx/Qq2wPjCuYISBueyVUGK7qqdmj7r/nxaxwW8AQDCLeRO7wZnjj94sh3p246cAYjUKuqgfg==} + engines: {node: '>=18.0.0', npm: '>=9.0.0'} + '@floating-ui/core@1.7.1': resolution: {integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==} @@ -324,6 +347,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@next/env@15.3.3': resolution: {integrity: sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==} @@ -653,6 +679,10 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@solana/wallet-standard-features@1.3.0': + resolution: {integrity: sha512-ZhpZtD+4VArf6RPitsVExvgkF+nGghd1rzPjd97GmBximpnt1rsUxMOEyoIEuH3XBxPyNB6Us7ha7RHWQR+abg==} + engines: {node: '>=16'} + '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -811,6 +841,18 @@ packages: '@tailwindcss/postcss@4.1.10': resolution: {integrity: sha512-B+7r7ABZbkXJwpvt2VMnS6ujcDoR2OOcFaqrLIo1xbcdxje4Vf+VgJdBzNNbrAjBj/rLZ66/tlQ1knIGNLKOBQ==} + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/node@20.19.0': resolution: {integrity: sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==} @@ -828,10 +870,80 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@wallet-standard/base@1.1.0': + resolution: {integrity: sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==} + engines: {node: '>=16'} + + '@wallet-standard/features@1.1.0': + resolution: {integrity: sha512-hiEivWNztx73s+7iLxsuD1sOJ28xtRix58W7Xnz4XzzA/pF0+aicnWgjOdA10doVDEDZdUuZCIIqG96SFNlDUg==} + engines: {node: '>=16'} + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@1.4.0: + resolution: {integrity: sha512-wiXutNjDUlNEDWHcYH3jtZUhd3c4/VojassD8zHdHCY13xbZy2XbW+NKQwA0tWGBVzDA9qEzYwfoSsWmviidhw==} + engines: {node: '>=0.10.0'} + + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + + ansi-styles@2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + aws-sign2@0.7.0: + resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} + + aws4@1.13.2: + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + biome@0.3.3: + resolution: {integrity: sha512-4LXjrQYbn9iTXu9Y4SKT7ABzTV0WnLDHCVSd2fPUOKsy1gQ+E4xPFmlY1zcWexoi0j7fGHItlL6OWA2CZ/yYAQ==} + hasBin: true + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -839,6 +951,13 @@ packages: caniuse-lite@1.0.30001722: resolution: {integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==} + caseless@0.12.0: + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + + chalk@1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -846,6 +965,13 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-cursor@1.0.2: + resolution: {integrity: sha512-25tABq090YNKkF6JH7lcwO0zFJTRke4Jcq9iX2nr/Sz0Cjjv4gckmwlW6Ty/aoyFd6z3ysR2hMGC2GFugmBo6A==} + engines: {node: '>=0.10.0'} + + cli-width@1.1.1: + resolution: {integrity: sha512-eMU2akIeEIkCxGXUNmDnJq1KzOIiPnJ+rKqRe6hcxE3vIOPvpMrBYOn/Bl7zNlYJj/zQxXquAnozHUCf9Whnsg==} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -853,6 +979,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + code-point-at@1.1.0: + resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} + engines: {node: '>=0.10.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -867,13 +997,41 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + core-js@2.6.12: + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dashdash@1.14.1: + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + detect-libc@2.0.4: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} @@ -881,24 +1039,155 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + engines: {node: '>=12'} + + earlgrey-runtime@0.1.2: + resolution: {integrity: sha512-T4qoScXi5TwALDv8nlGTvOuCT8jXcKcxtO8qVdqv46IA2GHJfQzwoBPbkOmORnyhu3A98cVVuhWLsM2CzPljJg==} + + ecc-jsbn@0.1.2: + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + + editor@1.0.0: + resolution: {integrity: sha512-SoRmbGStwNYHgKfjOrX2L0mUvp9bUVv0uPppZSOMAntEbcFtoC3MKF5b3T6HQPXKIV+QGY3xPO3JK5it5lVkuw==} + enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + exit-hook@1.1.1: + resolution: {integrity: sha512-MsG3prOVw1WtLXAZbM3KiYtooKR1LvxHh3VHsVtIy0uiUu8usxgB/94DP2HxtD/661lLdB6yzQ09lGJSQr6nkg==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extsprintf@1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + figures@1.7.0: + resolution: {integrity: sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==} + engines: {node: '>=0.10.0'} + + forever-agent@0.6.1: + resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + + form-data@2.3.3: + resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + engines: {node: '>= 0.12'} + + fs-extra@0.26.7: + resolution: {integrity: sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==} + + fs-promise@0.5.0: + resolution: {integrity: sha512-Y+4F4ujhEcayCJt6JmzcOun9MYGQwz+bVUiuBmTkJImhBHKpBvmVPZR9wtfiF7k3ffwAOAuurygQe+cPLSFQhw==} + deprecated: Use mz or fs-extra^3.0 with Promise Support + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} + getpass@0.1.7: + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + har-schema@2.0.0: + resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} + engines: {node: '>=4'} + + har-validator@5.1.5: + resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} + engines: {node: '>=6'} + deprecated: this library is no longer supported + + has-ansi@2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + + http-signature@1.2.0: + resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} + engines: {node: '>=0.8', npm: '>=1.3.7'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inquirer-promise@0.0.3: + resolution: {integrity: sha512-82CQX586JAV9GAgU9yXZsMDs+NorjA0nLhkfFx9+PReyOnuoHRbHrC1Z90sS95bFJI1Tm1gzMObuE0HabzkJpg==} + + inquirer@0.11.4: + resolution: {integrity: sha512-QR+2TW90jnKk9LUUtbcA3yQXKt2rDEKMh6+BAZQIeumtzHexnwVLdPakSslGijXYLJCzFv7GMXbFCn0pA00EUw==} + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-fullwidth-code-point@1.0.0: + resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} + engines: {node: '>=0.10.0'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + isstream@0.1.2: + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + jiti@2.4.2: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + jsbn@0.1.1: + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + jsonfile@2.4.0: + resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} + + jsprim@1.4.2: + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} + + kaiser@0.0.4: + resolution: {integrity: sha512-m8ju+rmBqvclZmyrOXgGGhOYSjKJK6RN1NhqEltemY87UqZOxEkizg9TOy1vQSyJ01Wx6SAPuuN0iO2Mgislvw==} + + klaw@1.3.1: + resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==} + lightningcss-darwin-arm64@1.30.1: resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} engines: {node: '>= 12.0.0'} @@ -963,6 +1252,12 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + lodash@3.10.1: + resolution: {integrity: sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lucide-react@0.514.0: resolution: {integrity: sha512-HXD0OAMd+JM2xCjlwG1EGW9Nuab64dhjO3+MvdyD+pSUeOTBaVAPhQblKIYmmX4RyBYbdzW0VWnJpjJmxWGr6w==} peerDependencies: @@ -971,6 +1266,20 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -987,6 +1296,12 @@ packages: engines: {node: '>=10'} hasBin: true + mute-stream@0.0.5: + resolution: {integrity: sha512-EbrziT4s8cWPmzr47eYVW3wimS4HsvlnV5ri1xw1aR6JQo/OrJX5rkl32K/QQHdxeabJETtfeaROGhd8W7uBgg==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1027,6 +1342,10 @@ packages: sass: optional: true + number-is-nan@1.0.1: + resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} + engines: {node: '>=0.10.0'} + nuqs@2.4.3: resolution: {integrity: sha512-BgtlYpvRwLYiJuWzxt34q2bXu/AIS66sLU1QePIMr2LWkb+XH0vKXdbLSgn9t6p7QKzwI7f38rX3Wl9llTXQ8Q==} peerDependencies: @@ -1045,6 +1364,31 @@ packages: react-router-dom: optional: true + oauth-sign@0.9.0: + resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@1.1.0: + resolution: {integrity: sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A==} + engines: {node: '>=0.10.0'} + + os-homedir@1.0.2: + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1056,6 +1400,17 @@ packages: resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==} engines: {node: ^10 || ^12 || >=14} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.5.3: + resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} + engines: {node: '>=0.6'} + react-dom@19.1.0: resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: @@ -1106,6 +1461,43 @@ packages: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} + readline2@1.0.1: + resolution: {integrity: sha512-8/td4MmwUB6PkZUbV25uKz7dfrmjYWxsW8DVfibWdlHRk/l/DfHKn4pU+dfcoGLFgWOdyGCzINRQD7jn+Bv+/g==} + + regenerator-runtime@0.9.6: + resolution: {integrity: sha512-D0Y/JJ4VhusyMOd/o25a3jdUqN/bC85EFsaoL9Oqmy/O4efCh+xhp7yj2EEOsj974qvMkcW8AwUzJ1jB/MbxCw==} + + request-promise@3.0.0: + resolution: {integrity: sha512-wVGUX+BoKxYsavTA72i6qHcyLbjzM4LR4y/AmDCqlbuMAursZdDWO7PmgbGAUvD2SeEJ5iB99VSq/U51i/DNbw==} + engines: {node: '>=0.10.0'} + deprecated: request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142 + + request@2.88.2: + resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} + engines: {node: '>= 6'} + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + + restore-cursor@1.0.1: + resolution: {integrity: sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw==} + engines: {node: '>=0.10.0'} + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + run-async@0.1.0: + resolution: {integrity: sha512-qOX+w+IxFgpUpJfkv2oGN0+ExPs68F4sZHfaRRx4dDexAQkG83atugKVEylyT5ARees3HBbfmuvnjbrd8j9Wjw==} + + rx-lite@3.1.2: + resolution: {integrity: sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} @@ -1131,10 +1523,23 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + sshpk@1.18.0: + resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} + engines: {node: '>=0.10.0'} + hasBin: true + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + string-width@1.0.2: + resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} + engines: {node: '>=0.10.0'} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -1148,6 +1553,10 @@ packages: babel-plugin-macros: optional: true + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} @@ -1162,15 +1571,49 @@ packages: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tough-cookie@2.5.0: + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + tw-animate-css@1.3.4: resolution: {integrity: sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg==} + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -1179,6 +1622,13 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + untildify@3.0.3: + resolution: {integrity: sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==} + engines: {node: '>=4'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -1199,12 +1649,31 @@ packages: '@types/react': optional: true + user-home@2.0.0: + resolution: {integrity: sha512-KMWqdlOcjCYdtIJpicDSFBQ8nFwS2i9sslAd6f4+CBGcU4gist2REnr2fxj2YocvJFxSF3ZOHLYLVZnUxv4BZQ==} + engines: {node: '>=0.10.0'} + + uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + verror@1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.2: resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} engines: {node: '>=10.0.0'} @@ -1221,6 +1690,10 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + zod@3.25.67: resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} @@ -1268,11 +1741,17 @@ snapshots: '@biomejs/cli-win32-x64@2.0.0': optional: true + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@emnapi/runtime@1.4.3': dependencies: tslib: 2.8.1 optional: true + '@faker-js/faker@9.8.0': {} + '@floating-ui/core@1.7.1': dependencies: '@floating-ui/utils': 0.2.9 @@ -1397,6 +1876,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@next/env@15.3.3': {} '@next/swc-darwin-arm64@15.3.3': @@ -1659,6 +2143,11 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@solana/wallet-standard-features@1.3.0': + dependencies: + '@wallet-standard/base': 1.1.0 + '@wallet-standard/features': 1.1.0 + '@standard-schema/utils@0.3.0': {} '@supabase/auth-js@2.70.0': @@ -1798,6 +2287,14 @@ snapshots: postcss: 8.5.5 tailwindcss: 4.1.10 + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + '@types/node@20.19.0': dependencies: undici-types: 6.21.0 @@ -1816,26 +2313,110 @@ snapshots: dependencies: '@types/node': 20.19.0 + '@wallet-standard/base@1.1.0': {} + + '@wallet-standard/features@1.1.0': + dependencies: + '@wallet-standard/base': 1.1.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@1.4.0: {} + + ansi-regex@2.1.1: {} + + ansi-styles@2.2.1: {} + + any-promise@1.3.0: {} + + arg@4.1.3: {} + aria-hidden@1.2.6: dependencies: tslib: 2.8.1 + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assert-plus@1.0.0: {} + + asynckit@0.4.0: {} + + aws-sign2@0.7.0: {} + + aws4@1.13.2: {} + + balanced-match@1.0.2: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + biome@0.3.3: + dependencies: + bluebird: 3.7.2 + chalk: 1.1.3 + commander: 2.20.3 + editor: 1.0.0 + fs-promise: 0.5.0 + inquirer-promise: 0.0.3 + request-promise: 3.0.0 + untildify: 3.0.3 + user-home: 2.0.0 + + bluebird@3.7.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + busboy@1.6.0: dependencies: streamsearch: 1.1.0 caniuse-lite@1.0.30001722: {} + caseless@0.12.0: {} + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + chownr@3.0.0: {} class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 + cli-cursor@1.0.2: + dependencies: + restore-cursor: 1.0.1 + + cli-width@1.1.1: {} + client-only@0.0.1: {} clsx@2.1.1: {} + code-point-at@1.1.0: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -1856,28 +2437,201 @@ snapshots: color-string: 1.9.1 optional: true + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@2.20.3: {} + + concat-map@0.0.1: {} + cookie@1.0.2: {} + core-js@2.6.12: {} + + core-util-is@1.0.2: {} + + create-require@1.1.1: {} + csstype@3.1.3: {} + dashdash@1.14.1: + dependencies: + assert-plus: 1.0.0 + + delayed-stream@1.0.0: {} + detect-libc@2.0.4: {} detect-node-es@1.1.0: {} + diff@4.0.2: {} + + dotenv@16.5.0: {} + + earlgrey-runtime@0.1.2: + dependencies: + core-js: 2.6.12 + kaiser: 0.0.4 + lodash: 4.17.21 + regenerator-runtime: 0.9.6 + + ecc-jsbn@0.1.2: + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + + editor@1.0.0: {} + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.2 + escape-string-regexp@1.0.5: {} + + exit-hook@1.1.1: {} + + extend@3.0.2: {} + + extsprintf@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + figures@1.7.0: + dependencies: + escape-string-regexp: 1.0.5 + object-assign: 4.1.1 + + forever-agent@0.6.1: {} + + form-data@2.3.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fs-extra@0.26.7: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 2.4.0 + klaw: 1.3.1 + path-is-absolute: 1.0.1 + rimraf: 2.7.1 + + fs-promise@0.5.0: + dependencies: + any-promise: 1.3.0 + fs-extra: 0.26.7 + mz: 2.7.0 + thenify-all: 1.6.0 + + fs.realpath@1.0.0: {} + get-nonce@1.0.1: {} + getpass@0.1.7: + dependencies: + assert-plus: 1.0.0 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + graceful-fs@4.2.11: {} + har-schema@2.0.0: {} + + har-validator@5.1.5: + dependencies: + ajv: 6.12.6 + har-schema: 2.0.0 + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + http-signature@1.2.0: + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.2 + sshpk: 1.18.0 + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inquirer-promise@0.0.3: + dependencies: + earlgrey-runtime: 0.1.2 + inquirer: 0.11.4 + + inquirer@0.11.4: + dependencies: + ansi-escapes: 1.4.0 + ansi-regex: 2.1.1 + chalk: 1.1.3 + cli-cursor: 1.0.2 + cli-width: 1.1.1 + figures: 1.7.0 + lodash: 3.10.1 + readline2: 1.0.1 + run-async: 0.1.0 + rx-lite: 3.1.2 + string-width: 1.0.2 + strip-ansi: 3.0.1 + through: 2.3.8 + is-arrayish@0.3.2: optional: true + is-fullwidth-code-point@1.0.0: + dependencies: + number-is-nan: 1.0.1 + + is-typedarray@1.0.0: {} + + isstream@0.1.2: {} + jiti@2.4.2: {} + jsbn@0.1.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema@0.4.0: {} + + json-stringify-safe@5.0.1: {} + + jsonfile@2.4.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsprim@1.4.2: + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + + kaiser@0.0.4: + dependencies: + earlgrey-runtime: 0.1.2 + + klaw@1.3.1: + optionalDependencies: + graceful-fs: 4.2.11 + lightningcss-darwin-arm64@1.30.1: optional: true @@ -1923,6 +2677,10 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + lodash@3.10.1: {} + + lodash@4.17.21: {} + lucide-react@0.514.0(react@19.1.0): dependencies: react: 19.1.0 @@ -1931,6 +2689,18 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + make-error@1.3.6: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + minipass@7.1.2: {} minizlib@3.0.2: @@ -1941,6 +2711,14 @@ snapshots: mkdirp@3.0.1: {} + mute-stream@0.0.5: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} next-safe-action@8.0.3(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): @@ -1979,6 +2757,8 @@ snapshots: - '@babel/core' - babel-plugin-macros + number-is-nan@1.0.1: {} + nuqs@2.4.3(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): dependencies: mitt: 3.0.1 @@ -1986,6 +2766,22 @@ snapshots: optionalDependencies: next: 15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + oauth-sign@0.9.0: {} + + object-assign@4.1.1: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@1.1.0: {} + + os-homedir@1.0.2: {} + + path-is-absolute@1.0.1: {} + + performance-now@2.1.0: {} + picocolors@1.1.1: {} postcss@8.4.31: @@ -2000,6 +2796,14 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode@2.3.1: {} + + qs@6.5.3: {} + react-dom@19.1.0(react@19.1.0): dependencies: react: 19.1.0 @@ -2042,6 +2846,62 @@ snapshots: react@19.1.0: {} + readline2@1.0.1: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + mute-stream: 0.0.5 + + regenerator-runtime@0.9.6: {} + + request-promise@3.0.0: + dependencies: + bluebird: 3.7.2 + lodash: 4.17.21 + request: 2.88.2 + + request@2.88.2: + dependencies: + aws-sign2: 0.7.0 + aws4: 1.13.2 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + har-validator: 5.1.5 + http-signature: 1.2.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + oauth-sign: 0.9.0 + performance-now: 2.1.0 + qs: 6.5.3 + safe-buffer: 5.2.1 + tough-cookie: 2.5.0 + tunnel-agent: 0.6.0 + uuid: 3.4.0 + + restore-cursor@1.0.1: + dependencies: + exit-hook: 1.1.1 + onetime: 1.1.0 + + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + + run-async@0.1.0: + dependencies: + once: 1.4.0 + + rx-lite@3.1.2: {} + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + scheduler@0.26.0: {} semver@7.7.2: @@ -2088,13 +2948,37 @@ snapshots: source-map-js@1.2.1: {} + sshpk@1.18.0: + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + streamsearch@1.1.0: {} + string-width@1.0.2: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + styled-jsx@5.1.6(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 + supports-color@2.0.0: {} + tailwind-merge@3.3.1: {} tailwindcss@4.1.10: {} @@ -2110,16 +2994,61 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + through@2.3.8: {} + + tough-cookie@2.5.0: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + tr46@0.0.3: {} + ts-node@10.9.2(@types/node@20.19.0)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.19.0 + acorn: 8.15.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + tslib@2.8.1: {} + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + tw-animate-css@1.3.4: {} + tweetnacl@0.14.5: {} + typescript@5.8.3: {} undici-types@6.21.0: {} + untildify@3.0.3: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + use-callback-ref@1.3.3(@types/react@19.1.8)(react@19.1.0): dependencies: react: 19.1.0 @@ -2135,6 +3064,20 @@ snapshots: optionalDependencies: '@types/react': 19.1.8 + user-home@2.0.0: + dependencies: + os-homedir: 1.0.2 + + uuid@3.4.0: {} + + v8-compile-cache-lib@3.0.1: {} + + verror@1.10.0: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + webidl-conversions@3.0.1: {} whatwg-url@5.0.0: @@ -2142,8 +3085,12 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + wrappy@1.0.2: {} + ws@8.18.2: {} yallist@5.0.0: {} + yn@3.1.1: {} + zod@3.25.67: {} diff --git a/src/tools/seed.ts b/src/tools/seed.ts new file mode 100644 index 0000000..ee58367 --- /dev/null +++ b/src/tools/seed.ts @@ -0,0 +1,104 @@ +import { config } from "dotenv"; +import { createClient, SupabaseClient } from "@supabase/supabase-js"; +import { faker } from "@faker-js/faker"; + +config(); // charge .env + +const supabase: SupabaseClient = createClient( + process.env.SUPABASE_URL!, + process.env.SUPABASE_KEY! +); + +// nombre d’éléments à générer +const N_ORG = 10; +const N_STUD = 30; +const N_INTERN = 20; +const N_COMPLETE = 50; + +async function seedOrganizations() { + const orgs = Array.from({ length: N_ORG }).map(() => ({ + organization_name: faker.company.name(), + tutor_firstname: faker.person.firstName(), + tutor_lastname: faker.person.lastName(), + organization_type: faker.company.buzzPhrase(), + organization_country: faker.location.country(), + organization_city: faker.location.city(), + organization_postal_code: Number(faker.location.zipCode()), + })); + + const { data, error } = await supabase + .from("Organization") + .insert(orgs) + .select("organization_id"); + if (error) throw error; + return data!.map((o) => o.organization_id); +} + +async function seedStudents(orgIds: number[]) { + const studs = Array.from({ length: N_STUD }).map(() => ({ + student_lastname: faker.person.lastName(), + student_firstname: faker.person.firstName(), + student_degree: faker.helpers.arrayElement([ + "Généraliste", + "IR", + "TI Santé", + ]), + student_course: faker.lorem.word(), + organization_id: faker.helpers.arrayElement(orgIds), + })); + const { data, error } = await supabase + .from("Student") + .insert(studs) + .select("student_id"); + if (error) throw error; + return data!.map((s) => s.student_id); +} + +async function seedInternships() { + const interns = Array.from({ length: N_INTERN }).map(() => ({ + internship_subject: faker.lorem.sentence(), + internship_confidential: faker.datatype.boolean(), + internship_dates: faker.date.past().toISOString(), + internship_period: faker.number.int({ min: 4, max: 24 }), + internship_year: faker.helpers.arrayElement(["1A", "2A", "3A"]), + })); + const { data, error } = await supabase + .from("Internship") + .insert(interns) + .select("internship_id"); + if (error) throw error; + return data!.map((i) => i.internship_id); +} + +async function seedComplete(studentIds: number[], internshipIds: number[]) { + const pairs = new Set(); + const completes = []; + while (completes.length < N_COMPLETE) { + const s = faker.helpers.arrayElement(studentIds); + const i = faker.helpers.arrayElement(internshipIds); + const key = `${i}-${s}`; + if (!pairs.has(key)) { + pairs.add(key); + completes.push({ internship_id: i, student_id: s }); + } + } + const { error } = await supabase.from("Complete").insert(completes); + if (error) throw error; +} + +async function main() { + console.log("→ Création des organisations…"); + const orgIds = await seedOrganizations(); + console.log("→ Création des étudiants…"); + const studIds = await seedStudents(orgIds); + console.log("→ Création des internships…"); + const internIds = await seedInternships(); + console.log(" → Création des liaisons Complete…"); + await seedComplete(studIds, internIds); + console.log("✅ Seeding terminé !"); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); From 874fc4a7470ad7e195f7815938734f1813c5276c Mon Sep 17 00:00:00 2001 From: ItsMii176 Date: Fri, 27 Jun 2025 16:46:49 +0200 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=97=83=EF=B8=8F=20Implement=20complet?= =?UTF-8?q?e=20database=20seeding=20system=20with=20Drizzle=20ORM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add PostgreSQL schema with foreign key relationships (organizations, students, internships, complete) - Implement reproducible data seeding with Faker.js - Create database cleanup utilities for clean development workflow - Add comprehensive data display functions with relational queries - Configure Drizzle Kit for database migrations and schema management - Set up TypeScript compilation and npm scripts for seed/clean/query operations - Add proper .gitignore rules for node_modules and build artifacts - Ensure 1:1 student-internship relationships for independent data modeling --- .gitignore | 25 + backend/drizzle.config.ts | 16 + backend/package-lock.json | 392 ++++++++++ backend/package.json | 23 + .../projetParseur}/.gitignore | 0 ...5-R\303\251capitulatif des stages 2A.xlsx" | Bin .../projetParseur}/package.json | 0 .../projetParseur}/pnpm-lock.yaml | 0 .../projetParseur}/src/functions.ts | 0 .../projetParseur}/src/index.ts | 0 .../projetParseur}/src/parserInternship2A.ts | 0 .../src/parserSubstitutionInternship.ts | 0 .../projetParseur}/src/typeDefinition.ts | 0 .../projetParseur}/tsconfig.json | 0 backend/src/clean.ts | 54 ++ backend/src/db/index.ts | 12 + backend/src/db/schema.ts | 57 ++ backend/src/index.ts | 27 + backend/src/queries.ts | 135 ++++ backend/src/seed.ts | 235 ++++++ backend/src/utils.ts | 19 + backend/tsconfig.json | 16 + package.json | 4 + pnpm-lock.yaml | 717 ++++++++++++++++++ src/tools/seed.ts | 27 +- 25 files changed, 1739 insertions(+), 20 deletions(-) create mode 100644 backend/drizzle.config.ts create mode 100644 backend/package-lock.json create mode 100644 backend/package.json rename {projetParseur => backend/projetParseur}/.gitignore (100%) rename "projetParseur/2024-25-R\303\251capitulatif des stages 2A.xlsx" => "backend/projetParseur/2024-25-R\303\251capitulatif des stages 2A.xlsx" (100%) rename {projetParseur => backend/projetParseur}/package.json (100%) rename {projetParseur => backend/projetParseur}/pnpm-lock.yaml (100%) rename {projetParseur => backend/projetParseur}/src/functions.ts (100%) rename {projetParseur => backend/projetParseur}/src/index.ts (100%) rename {projetParseur => backend/projetParseur}/src/parserInternship2A.ts (100%) rename {projetParseur => backend/projetParseur}/src/parserSubstitutionInternship.ts (100%) rename {projetParseur => backend/projetParseur}/src/typeDefinition.ts (100%) rename {projetParseur => backend/projetParseur}/tsconfig.json (100%) create mode 100644 backend/src/clean.ts create mode 100644 backend/src/db/index.ts create mode 100644 backend/src/db/schema.ts create mode 100644 backend/src/index.ts create mode 100644 backend/src/queries.ts create mode 100644 backend/src/seed.ts create mode 100644 backend/src/utils.ts create mode 100644 backend/tsconfig.json diff --git a/.gitignore b/.gitignore index c021640..0f4e988 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,11 @@ !.yarn/releases !.yarn/versions +# backend dependencies +/backend/node_modules +/backend/dist +/backend/*.tsbuildinfo + # testing /coverage @@ -43,3 +48,23 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# Database files +*.sqlite +*.db + +# Drizzle generated files +/backend/drizzle/migrations/* +!/backend/drizzle/migrations/.gitkeep + +# IDE +.vscode/ +.idea/ + +# OS generated files +Thumbs.db +ehthumbs.db + +# Temporary files +*.tmp +*.temp diff --git a/backend/drizzle.config.ts b/backend/drizzle.config.ts new file mode 100644 index 0000000..485a87d --- /dev/null +++ b/backend/drizzle.config.ts @@ -0,0 +1,16 @@ +import { config } from "dotenv"; +import { defineConfig } from "drizzle-kit"; + +config({ path: ".env" }); + +console.log("DATABASE_URL:", process.env.DATABASE_URL); + +export default defineConfig({ + schema: "./src/db/schema.ts", + out: "./drizzle/migrations", + dialect: "postgresql", + dbCredentials: { + url: process.env.DATABASE_URL!, + ssl: { rejectUnauthorized: false }, // Ajoute cette ligne + }, +}); diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..c135298 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,392 @@ +{ + "name": "backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "1.0.0", + "dependencies": { + "@faker-js/faker": "^8.0.0", + "dotenv": "^16.0.0", + "drizzle-orm": "^0.30.0", + "postgres": "^3.4.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "ts-node": "^10.9.0", + "typescript": "^5.0.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@faker-js/faker": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", + "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0", + "npm": ">=6.14.13" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", + "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.0.tgz", + "integrity": "sha512-Omf1L8paOy2VJhILjyhrhqwLIdstqm1BvcDPKg4NGAlkwEu9ODyrFbvk8UymUOMCT+HXo31jg1lArIrVAAhuGA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/drizzle-orm": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.30.10.tgz", + "integrity": "sha512-IRy/QmMWw9lAQHpwbUh1b8fcn27S/a9zMIzqea1WNOxK9/4EB8gIo+FZWLiPXzl2n9ixGSv8BhsLZiOppWEwBw==", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=3", + "@electric-sql/pglite": ">=0.1.1", + "@libsql/client": "*", + "@neondatabase/serverless": ">=0.1", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/react": ">=18", + "@types/sql.js": "*", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=13.2.0", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "react": ">=18", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "react": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/postgres": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..b0a13dd --- /dev/null +++ b/backend/package.json @@ -0,0 +1,23 @@ +{ + "name": "backend", + "version": "1.0.0", + "type": "module", + "scripts": { + "build": "tsc", + "seed": "pnpm run build && node dist/src/seed.js", + "clean": "pnpm run build && node dist/src/clean.js", + "query": "pnpm run build && node dist/src/queries.js", + "start": "pnpm run build && node dist/src/index.js" + }, + "dependencies": { + "drizzle-orm": "^0.30.0", + "postgres": "^3.4.0", + "@faker-js/faker": "^8.0.0", + "dotenv": "^16.0.0" + }, + "devDependencies": { + "ts-node": "^10.9.0", + "typescript": "^5.0.0", + "@types/node": "^20.0.0" + } +} diff --git a/projetParseur/.gitignore b/backend/projetParseur/.gitignore similarity index 100% rename from projetParseur/.gitignore rename to backend/projetParseur/.gitignore diff --git "a/projetParseur/2024-25-R\303\251capitulatif des stages 2A.xlsx" "b/backend/projetParseur/2024-25-R\303\251capitulatif des stages 2A.xlsx" similarity index 100% rename from "projetParseur/2024-25-R\303\251capitulatif des stages 2A.xlsx" rename to "backend/projetParseur/2024-25-R\303\251capitulatif des stages 2A.xlsx" diff --git a/projetParseur/package.json b/backend/projetParseur/package.json similarity index 100% rename from projetParseur/package.json rename to backend/projetParseur/package.json diff --git a/projetParseur/pnpm-lock.yaml b/backend/projetParseur/pnpm-lock.yaml similarity index 100% rename from projetParseur/pnpm-lock.yaml rename to backend/projetParseur/pnpm-lock.yaml diff --git a/projetParseur/src/functions.ts b/backend/projetParseur/src/functions.ts similarity index 100% rename from projetParseur/src/functions.ts rename to backend/projetParseur/src/functions.ts diff --git a/projetParseur/src/index.ts b/backend/projetParseur/src/index.ts similarity index 100% rename from projetParseur/src/index.ts rename to backend/projetParseur/src/index.ts diff --git a/projetParseur/src/parserInternship2A.ts b/backend/projetParseur/src/parserInternship2A.ts similarity index 100% rename from projetParseur/src/parserInternship2A.ts rename to backend/projetParseur/src/parserInternship2A.ts diff --git a/projetParseur/src/parserSubstitutionInternship.ts b/backend/projetParseur/src/parserSubstitutionInternship.ts similarity index 100% rename from projetParseur/src/parserSubstitutionInternship.ts rename to backend/projetParseur/src/parserSubstitutionInternship.ts diff --git a/projetParseur/src/typeDefinition.ts b/backend/projetParseur/src/typeDefinition.ts similarity index 100% rename from projetParseur/src/typeDefinition.ts rename to backend/projetParseur/src/typeDefinition.ts diff --git a/projetParseur/tsconfig.json b/backend/projetParseur/tsconfig.json similarity index 100% rename from projetParseur/tsconfig.json rename to backend/projetParseur/tsconfig.json diff --git a/backend/src/clean.ts b/backend/src/clean.ts new file mode 100644 index 0000000..d5963b5 --- /dev/null +++ b/backend/src/clean.ts @@ -0,0 +1,54 @@ +import { config } from "dotenv"; +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; +import { completeTable, internshipsTable, organizationsTable, studentsTable } from "./db/schema.js"; + +// Charge les variables d'environnement +config({ path: ".env" }); + +// Configuration de la base de données +if (!process.env.DATABASE_URL) { + throw new Error("DATABASE_URL is required"); +} +const client = postgres(process.env.DATABASE_URL); +const db = drizzle(client); + +async function cleanDatabase() { + console.log("🧹 Nettoyage complet de la base de données..."); + + try { + // Supprime dans l'ordre inverse des contraintes (relations d'abord) + await db.delete(completeTable); + console.log(" → Table Complete vidée"); + + await db.delete(internshipsTable); + console.log(" → Table Internship vidée"); + + await db.delete(studentsTable); + console.log(" → Table Student vidée"); + + await db.delete(organizationsTable); + console.log(" → Table Organization vidée"); + + console.log("✅ Base de données nettoyée avec succès !"); + } catch (error) { + console.error("❌ Erreur lors du nettoyage:", error); + throw error; + } +} + +async function main() { + try { + await cleanDatabase(); + + // Ferme la connexion + await client.end(); + + console.log("\n🎉 Nettoyage terminé !"); + } catch (error) { + console.error("❌ Erreur:", error); + process.exit(1); + } +} + +main(); diff --git a/backend/src/db/index.ts b/backend/src/db/index.ts new file mode 100644 index 0000000..c2c6862 --- /dev/null +++ b/backend/src/db/index.ts @@ -0,0 +1,12 @@ +import { config } from "dotenv"; +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; + +config({ path: ".env" }); // or .env.local + +if (!process.env.DATABASE_URL) { + throw new Error("DATABASE_URL is required"); +} + +export const client = postgres(process.env.DATABASE_URL); +export const db = drizzle(client); diff --git a/backend/src/db/schema.ts b/backend/src/db/schema.ts new file mode 100644 index 0000000..276ba1f --- /dev/null +++ b/backend/src/db/schema.ts @@ -0,0 +1,57 @@ +import { boolean, foreignKey, integer, pgTable, serial, text } from "drizzle-orm/pg-core"; + +export const organizationsTable = pgTable("Organization", { + organization_id: serial("organization_id").primaryKey(), + organization_name: text("organization_name").notNull(), + tutor_firstname: text("tutor_firstname").notNull(), + tutor_lastname: text("tutor_lastname").notNull(), + organization_type: text("organization_type").notNull(), + organization_country: text("organization_country").notNull(), + organization_city: text("organization_city").notNull(), + organization_postal_code: integer("organization_postal_code").notNull(), +}); + +export const studentsTable = pgTable( + "Student", + { + student_id: serial("student_id").primaryKey(), + student_firstname: text("student_firstname").notNull(), + student_lastname: text("student_lastname").notNull(), + student_degree: text("student_degree").notNull(), + student_course: text("student_course").notNull(), + organization_id: integer("organization_id").notNull(), + }, + (table) => ({ + orgFK: foreignKey({ + columns: [table.organization_id], + foreignColumns: [organizationsTable.organization_id], + }), + }), +); + +export const internshipsTable = pgTable("Internship", { + internship_id: serial("internship_id").primaryKey(), + internship_subject: text("internship_subject").notNull(), + internship_confidential: boolean("internship_confidential").notNull(), + internship_dates: text("internship_dates").notNull(), + internship_period: integer("internship_period").notNull(), + internship_year: text("internship_year").notNull(), +}); + +export const completeTable = pgTable( + "Complete", + { + internship_id: integer("internship_id").notNull(), + student_id: integer("student_id").notNull(), + }, + (table) => ({ + internshipFK: foreignKey({ + columns: [table.internship_id], + foreignColumns: [internshipsTable.internship_id], + }), + studentFK: foreignKey({ + columns: [table.student_id], + foreignColumns: [studentsTable.student_id], + }), + }), +); diff --git a/backend/src/index.ts b/backend/src/index.ts new file mode 100644 index 0000000..23de8f4 --- /dev/null +++ b/backend/src/index.ts @@ -0,0 +1,27 @@ +import { closeConnection, displayDatabaseSummary, displayFirstInternship } from "./queries.js"; + +/** + * Point d'entrée principal de l'application + * Utilise les fonctions de queries.ts pour afficher les données + */ +async function main() { + try { + console.log("🚀 CHRONOS NOVA - Affichage des données"); + + // Affiche un résumé de la base + await displayDatabaseSummary(); + + // Affiche les détails du premier stage existant (pas forcément ID 1) + await displayFirstInternship(); + + // Ferme la connexion + await closeConnection(); + + console.log("\n✅ Terminé !"); + } catch (error) { + console.error("❌ Erreur:", error); + process.exit(1); + } +} + +main(); diff --git a/backend/src/queries.ts b/backend/src/queries.ts new file mode 100644 index 0000000..70e60ca --- /dev/null +++ b/backend/src/queries.ts @@ -0,0 +1,135 @@ +import { eq } from "drizzle-orm"; +import { client, db } from "./db/index.js"; +import { completeTable, internshipsTable, organizationsTable, studentsTable } from "./db/schema.js"; + +/** + * Affiche les détails du premier stage disponible dans la base + */ +export async function displayFirstInternship() { + try { + // Récupère le premier stage disponible + const firstInternship = await db.select().from(internshipsTable).limit(1); + + if (firstInternship.length === 0) { + console.log("\n❌ Aucun stage trouvé dans la base de données"); + return; + } + + // Affiche les détails de ce stage + await displayInternshipDetails(firstInternship[0].internship_id); + } catch (error) { + console.error("❌ Erreur lors de la recherche du premier stage:", error); + } +} + +/** + * Affiche les détails complets d'un stage avec tous les étudiants assignés + */ +export async function displayInternshipDetails(internshipId: number) { + console.log(`\n🔍 DÉTAILS COMPLETS DU STAGE ID: ${internshipId}`); + console.log("=".repeat(60)); + + try { + // Récupère le stage + const internship = await db + .select() + .from(internshipsTable) + .where(eq(internshipsTable.internship_id, internshipId)) + .limit(1); + + if (internship.length === 0) { + console.log("❌ Stage non trouvé"); + return; + } + + const stage = internship[0]; + console.log("\n📋 INFORMATIONS DU STAGE:"); + console.log(` ID: ${stage.internship_id}`); + console.log(` Sujet: ${stage.internship_subject}`); + console.log(` Année: ${stage.internship_year}`); + console.log(` Date: ${stage.internship_dates}`); + console.log(` Durée: ${stage.internship_period} semaines`); + console.log(` Confidentiel: ${stage.internship_confidential ? "Oui" : "Non"}`); + + // Récupère tous les étudiants assignés à ce stage + const studentsData = await db + .select({ + student: studentsTable, + organization: organizationsTable, + }) + .from(completeTable) + .innerJoin(studentsTable, eq(completeTable.student_id, studentsTable.student_id)) + .innerJoin( + organizationsTable, + eq(studentsTable.organization_id, organizationsTable.organization_id), + ) + .where(eq(completeTable.internship_id, internshipId)); + + console.log(`\n👥 ÉTUDIANTS ASSIGNÉS (${studentsData.length}):`); + if (studentsData.length === 0) { + console.log(" Aucun étudiant assigné à ce stage"); + } else { + for (const { student, organization } of studentsData) { + console.log(`\n • ${student.student_firstname} ${student.student_lastname}`); + console.log(` ID Étudiant: ${student.student_id}`); + console.log(` Filière: ${student.student_degree}`); + console.log(` Parcours: ${student.student_course}`); + console.log(` Organisation: ${organization.organization_name}`); + console.log(` Tuteur: ${organization.tutor_firstname} ${organization.tutor_lastname}`); + console.log( + ` Lieu: ${organization.organization_city}, ${organization.organization_country}`, + ); + } + } + + console.log("\n" + "=".repeat(60)); + } catch (error) { + console.error("❌ Erreur lors de l'affichage du stage:", error); + } +} + +/** + * Affiche un résumé de toutes les données + */ +export async function displayDatabaseSummary() { + console.log("\n📊 RÉSUMÉ DE LA BASE DE DONNÉES"); + console.log("=".repeat(40)); + + try { + const orgCount = await db.select().from(organizationsTable); + const studCount = await db.select().from(studentsTable); + const internCount = await db.select().from(internshipsTable); + const completeCount = await db.select().from(completeTable); + + console.log(`🏢 Organisations: ${orgCount.length}`); + console.log(`👥 Étudiants: ${studCount.length}`); + console.log(`🎯 Stages: ${internCount.length}`); + console.log(`🔗 Relations: ${completeCount.length}`); + } catch (error) { + console.error("❌ Erreur lors du résumé:", error); + } +} + +/** + * Ferme la connexion à la base de données + */ +export async function closeConnection() { + await client.end(); +} + +// Fonction principale pour exécuter des requêtes +async function main() { + try { + await displayDatabaseSummary(); + await displayFirstInternship(); + await closeConnection(); + } catch (error) { + console.error("❌ Erreur:", error); + process.exit(1); + } +} + +// Si ce fichier est exécuté directement +if (import.meta.url === new URL(import.meta.resolve("./queries.js")).href) { + main(); +} diff --git a/backend/src/seed.ts b/backend/src/seed.ts new file mode 100644 index 0000000..a116c2a --- /dev/null +++ b/backend/src/seed.ts @@ -0,0 +1,235 @@ +import { faker } from "@faker-js/faker"; +import { config } from "dotenv"; +import { eq } from "drizzle-orm"; +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; +import { completeTable, internshipsTable, organizationsTable, studentsTable } from "./db/schema.js"; +import { formatDateFR } from "./utils.js"; + +// Charge les variables d'environnement +config({ path: ".env" }); + +// Configuration de la base de données +if (!process.env.DATABASE_URL) { + throw new Error("DATABASE_URL is required"); +} +const client = postgres(process.env.DATABASE_URL); +const db = drizzle(client); + +// Seed fixe pour avoir des données reproductibles +faker.seed(12345); + +// Nombre d'éléments à générer +const N_ORG = 5; +const N_STUD = 8; +const N_COMPLETE = 8; + +async function cleanDatabase() { + console.log("🧹 Nettoyage de la base de données..."); + + try { + // Supprime dans l'ordre inverse des contraintes (relations d'abord) + await db.delete(completeTable); + console.log(" → Table Complete vidée"); + + await db.delete(internshipsTable); + console.log(" → Table Internship vidée"); + + await db.delete(studentsTable); + console.log(" → Table Student vidée"); + + await db.delete(organizationsTable); + console.log(" → Table Organization vidée"); + + console.log("✅ Base de données nettoyée avec succès !"); + } catch (error) { + console.error("❌ Erreur lors du nettoyage:", error); + throw error; + } +} + +async function seedOrganizations() { + console.log("→ Création des organisations..."); + + const orgs = Array.from({ length: N_ORG }).map(() => ({ + organization_name: faker.company.name(), + tutor_firstname: faker.person.firstName(), + tutor_lastname: faker.person.lastName(), + organization_type: faker.company.buzzPhrase(), + organization_country: faker.location.country(), + organization_city: faker.location.city(), + organization_postal_code: faker.number.int({ min: 10000, max: 99999 }), + })); + + const result = await db + .insert(organizationsTable) + .values(orgs) + .returning({ id: organizationsTable.organization_id }); + + return result.map((row) => row.id); +} + +async function seedStudents(orgIds: number[]) { + console.log("→ Création des étudiants..."); + + const students = Array.from({ length: N_STUD }).map(() => ({ + student_firstname: faker.person.firstName(), + student_lastname: faker.person.lastName(), + student_degree: faker.helpers.arrayElement(["Généraliste", "IR", "TI Santé"]), + student_course: faker.helpers.arrayElement([ + "Informatique", + "Réseaux", + "Cybersécurité", + "IA", + "Biomédical", + "E-santé", + "Développement web", + "Systèmes embarqués", + "Data Science", + ]), + organization_id: faker.helpers.arrayElement(orgIds), + })); + + const result = await db + .insert(studentsTable) + .values(students) + .returning({ id: studentsTable.student_id }); + + return result.map((row) => row.id); +} + +async function seedComplete(studentIds: number[]) { + console.log("→ Création des relations étudiant-stage indépendantes..."); + + const completes = []; + let lastInternshipId = 0; + + // Sujets de stages réalistes pour l'informatique + const internshipSubjects = [ + "Développement d'une application mobile de gestion", + "Sécurisation du réseau informatique de l'entreprise", + "Analyse de données avec intelligence artificielle", + "Création d'un chatbot pour le service client", + "Audit de sécurité des systèmes critiques", + "Interface web pour téléconsultation médicale", + "Optimisation des performances d'un site e-commerce", + "Programmation de capteurs IoT pour la domotique", + "Migration vers le cloud des services internes", + "Développement d'une API REST pour mobile", + ]; + + for (let i = 0; i < N_COMPLETE; i++) { + // Crée un nouveau stage pour chaque relation avec un étudiant unique + const newInternship = await db + .insert(internshipsTable) + .values({ + internship_subject: faker.helpers.arrayElement(internshipSubjects), + internship_confidential: faker.datatype.boolean(), + internship_dates: formatDateFR(faker.date.past()), + internship_period: faker.number.int({ min: 8, max: 24 }), + internship_year: faker.helpers.arrayElement(["1A", "2A", "3A"]), + }) + .returning({ id: internshipsTable.internship_id }); + + lastInternshipId = newInternship[0].id; + + // Chaque étudiant n'a qu'un seul stage (relation 1-1) + completes.push({ + student_id: studentIds[i], // Utilise l'index pour garantir l'unicité + internship_id: newInternship[0].id, + }); + } + + await db.insert(completeTable).values(completes); + console.log(` → ${completes.length} relations étudiant-stage indépendantes créées`); + return lastInternshipId; +} + +async function displayInternshipDetails(internshipId: number) { + console.log(`\n🔍 DÉTAILS COMPLETS DU STAGE ID: ${internshipId}`); + console.log("=".repeat(60)); + + try { + // Récupère le stage + const internship = await db + .select() + .from(internshipsTable) + .where(eq(internshipsTable.internship_id, internshipId)) + .limit(1); + + if (internship.length === 0) { + console.log("❌ Stage non trouvé"); + return; + } + + const stage = internship[0]; + console.log("\n📋 INFORMATIONS DU STAGE:"); + console.log(` ID: ${stage.internship_id}`); + console.log(` Sujet: ${stage.internship_subject}`); + console.log(` Année: ${stage.internship_year}`); + console.log(` Date: ${stage.internship_dates}`); + console.log(` Durée: ${stage.internship_period} semaines`); + console.log(` Confidentiel: ${stage.internship_confidential ? "Oui" : "Non"}`); + + // Récupère tous les étudiants assignés à ce stage + const studentsData = await db + .select({ + student: studentsTable, + organization: organizationsTable, + }) + .from(completeTable) + .innerJoin(studentsTable, eq(completeTable.student_id, studentsTable.student_id)) + .innerJoin( + organizationsTable, + eq(studentsTable.organization_id, organizationsTable.organization_id), + ) + .where(eq(completeTable.internship_id, internshipId)); + + console.log(`\n👥 ÉTUDIANTS ASSIGNÉS (${studentsData.length}):`); + if (studentsData.length === 0) { + console.log(" Aucun étudiant assigné à ce stage"); + } else { + for (const { student, organization } of studentsData) { + console.log(`\n • ${student.student_firstname} ${student.student_lastname}`); + console.log(` ID Étudiant: ${student.student_id}`); + console.log(` Filière: ${student.student_degree}`); + console.log(` Parcours: ${student.student_course}`); + console.log(` Organisation: ${organization.organization_name}`); + console.log(` Tuteur: ${organization.tutor_firstname} ${organization.tutor_lastname}`); + console.log( + ` Lieu: ${organization.organization_city}, ${organization.organization_country}`, + ); + } + } + + console.log("\n" + "=".repeat(60)); + } catch (error) { + console.error("❌ Erreur lors de l'affichage du stage:", error); + } +} + +async function main() { + try { + console.log("🌱 Début du seeding..."); + + await cleanDatabase(); // Ajout du nettoyage de la base de données + + const orgIds = await seedOrganizations(); + const studentIds = await seedStudents(orgIds); + // Suppression de seedInternships() - stages créés uniquement dans seedComplete + const lastInternshipId = await seedComplete(studentIds); + + console.log("✅ Seeding terminé avec succès !"); + + // Affiche les détails du dernier stage créé (devrait avoir 1 seul étudiant) + await displayInternshipDetails(lastInternshipId); + + // Ferme la connexion + await client.end(); + } catch (error) { + console.error("❌ Erreur lors du seeding:", error); + process.exit(1); + } +} + +main(); diff --git a/backend/src/utils.ts b/backend/src/utils.ts new file mode 100644 index 0000000..d6a0e5b --- /dev/null +++ b/backend/src/utils.ts @@ -0,0 +1,19 @@ +/** + * Utilitaires pour la gestion des dates + */ + +/** + * Formate une date au format français DD/MM/YYYY + */ +export function formatDateFR(date: Date): string { + return `${date.getDate().toString().padStart(2, "0")}/${(date.getMonth() + 1).toString().padStart(2, "0")}/${date.getFullYear()}`; +} + +/** + * Génère une date aléatoire dans le passé formatée en français + */ +export function generateRandomPastDateFR(): string { + const now = new Date(); + const pastDate = new Date(now.getTime() - Math.random() * 365 * 24 * 60 * 60 * 1000); + return formatDateFR(pastDate); +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..1ad6e8a --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "outDir": "./dist" + }, + "ts-node": { + "esm": true + } +} diff --git a/package.json b/package.json index 29ea283..a89dea3 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,13 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^16.5.0", + "drizzle-orm": "^0.44.2", "lucide-react": "^0.514.0", "next": "15.3.3", "next-safe-action": "^8.0.3", "next-themes": "^0.4.6", "nuqs": "^2.4.3", + "postgres": "^3.4.7", "react": "^19.0.0", "react-dom": "^19.0.0", "react-hook-form": "^7.58.1", @@ -42,8 +44,10 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "drizzle-kit": "^0.31.4", "tailwindcss": "^4", "ts-node": "^10.9.2", + "tsx": "^4.20.3", "tw-animate-css": "^1.3.4", "typescript": "^5" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fe0247c..0939b72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: dotenv: specifier: ^16.5.0 version: 16.5.0 + drizzle-orm: + specifier: ^0.44.2 + version: 0.44.2(postgres@3.4.7) lucide-react: specifier: ^0.514.0 version: 0.514.0(react@19.1.0) @@ -65,6 +68,9 @@ importers: nuqs: specifier: ^2.4.3 version: 2.4.3(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + postgres: + specifier: ^3.4.7 + version: 3.4.7 react: specifier: ^19.0.0 version: 19.1.0 @@ -102,12 +108,18 @@ importers: '@types/react-dom': specifier: ^19 version: 19.1.6(@types/react@19.1.8) + drizzle-kit: + specifier: ^0.31.4 + version: 0.31.4 tailwindcss: specifier: ^4 version: 4.1.10 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.19.0)(typescript@5.8.3) + tsx: + specifier: ^4.20.3 + version: 4.20.3 tw-animate-css: specifier: ^1.3.4 version: 1.3.4 @@ -182,9 +194,302 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + '@emnapi/runtime@1.4.3': resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@faker-js/faker@9.8.0': resolution: {integrity: sha512-U9wpuSrJC93jZBxx/Qq2wPjCuYISBueyVUGK7qqdmj7r/nxaxwW8AQDCLeRO7wZnjj94sh3p246cAYjUKuqgfg==} engines: {node: '>=18.0.0', npm: '>=9.0.0'} @@ -944,6 +1249,9 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1028,6 +1336,15 @@ packages: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1047,6 +1364,102 @@ packages: resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} + drizzle-kit@0.31.4: + resolution: {integrity: sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==} + hasBin: true + + drizzle-orm@0.44.2: + resolution: {integrity: sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==} + 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.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + earlgrey-runtime@0.1.2: resolution: {integrity: sha512-T4qoScXi5TwALDv8nlGTvOuCT8jXcKcxtO8qVdqv46IA2GHJfQzwoBPbkOmORnyhu3A98cVVuhWLsM2CzPljJg==} @@ -1060,6 +1473,21 @@ packages: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} + hasBin: true + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1102,10 +1530,18 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} @@ -1296,6 +1732,9 @@ packages: engines: {node: '>=10'} hasBin: true + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mute-stream@0.0.5: resolution: {integrity: sha512-EbrziT4s8cWPmzr47eYVW3wimS4HsvlnV5ri1xw1aR6JQo/OrJX5rkl32K/QQHdxeabJETtfeaROGhd8W7uBgg==} @@ -1400,6 +1839,10 @@ packages: resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==} engines: {node: ^10 || ^12 || >=14} + postgres@3.4.7: + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} + psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -1477,6 +1920,9 @@ packages: engines: {node: '>= 6'} deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + restore-cursor@1.0.1: resolution: {integrity: sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw==} engines: {node: '>=0.10.0'} @@ -1523,6 +1969,13 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + sshpk@1.18.0: resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} engines: {node: '>=0.10.0'} @@ -1605,6 +2058,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + engines: {node: '>=18.0.0'} + hasBin: true + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -1745,11 +2203,164 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@drizzle-team/brocli@0.10.2': {} + '@emnapi/runtime@1.4.3': dependencies: tslib: 2.8.1 optional: true + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.10.1 + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.25.5': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.25.5': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.25.5': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.25.5': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.25.5': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.25.5': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.25.5': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.25.5': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.25.5': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.25.5': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.25.5': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.25.5': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.25.5': + optional: true + + '@esbuild/netbsd-arm64@0.25.5': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.25.5': + optional: true + + '@esbuild/openbsd-arm64@0.25.5': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.25.5': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.25.5': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.25.5': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.25.5': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@esbuild/win32-x64@0.25.5': + optional: true + '@faker-js/faker@9.8.0': {} '@floating-ui/core@1.7.1': @@ -2383,6 +2994,8 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + buffer-from@1.1.2: {} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -2459,6 +3072,10 @@ snapshots: dependencies: assert-plus: 1.0.0 + debug@4.4.1: + dependencies: + ms: 2.1.3 + delayed-stream@1.0.0: {} detect-libc@2.0.4: {} @@ -2469,6 +3086,19 @@ snapshots: dotenv@16.5.0: {} + drizzle-kit@0.31.4: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.5 + esbuild-register: 3.6.0(esbuild@0.25.5) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.44.2(postgres@3.4.7): + optionalDependencies: + postgres: 3.4.7 + earlgrey-runtime@0.1.2: dependencies: core-js: 2.6.12 @@ -2488,6 +3118,66 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.2 + esbuild-register@3.6.0(esbuild@0.25.5): + dependencies: + debug: 4.4.1 + esbuild: 0.25.5 + transitivePeerDependencies: + - supports-color + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + escape-string-regexp@1.0.5: {} exit-hook@1.1.1: {} @@ -2530,8 +3220,15 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.3: + optional: true + get-nonce@1.0.1: {} + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + getpass@0.1.7: dependencies: assert-plus: 1.0.0 @@ -2711,6 +3408,8 @@ snapshots: mkdirp@3.0.1: {} + ms@2.1.3: {} + mute-stream@0.0.5: {} mz@2.7.0: @@ -2796,6 +3495,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postgres@3.4.7: {} + psl@1.15.0: dependencies: punycode: 2.3.1 @@ -2883,6 +3584,8 @@ snapshots: tunnel-agent: 0.6.0 uuid: 3.4.0 + resolve-pkg-maps@1.0.0: {} + restore-cursor@1.0.1: dependencies: exit-hook: 1.1.1 @@ -2948,6 +3651,13 @@ snapshots: source-map-js@1.2.1: {} + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + sshpk@1.18.0: dependencies: asn1: 0.2.6 @@ -3031,6 +3741,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.20.3: + dependencies: + esbuild: 0.25.5 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 diff --git a/src/tools/seed.ts b/src/tools/seed.ts index ee58367..093f784 100644 --- a/src/tools/seed.ts +++ b/src/tools/seed.ts @@ -1,13 +1,10 @@ -import { config } from "dotenv"; -import { createClient, SupabaseClient } from "@supabase/supabase-js"; import { faker } from "@faker-js/faker"; +import { createClient, type SupabaseClient } from "@supabase/supabase-js"; +import { config } from "dotenv"; config(); // charge .env -const supabase: SupabaseClient = createClient( - process.env.SUPABASE_URL!, - process.env.SUPABASE_KEY! -); +const supabase: SupabaseClient = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_KEY!); // nombre d’éléments à générer const N_ORG = 10; @@ -38,18 +35,11 @@ async function seedStudents(orgIds: number[]) { const studs = Array.from({ length: N_STUD }).map(() => ({ student_lastname: faker.person.lastName(), student_firstname: faker.person.firstName(), - student_degree: faker.helpers.arrayElement([ - "Généraliste", - "IR", - "TI Santé", - ]), + student_degree: faker.helpers.arrayElement(["Généraliste", "IR", "TI Santé"]), student_course: faker.lorem.word(), organization_id: faker.helpers.arrayElement(orgIds), })); - const { data, error } = await supabase - .from("Student") - .insert(studs) - .select("student_id"); + const { data, error } = await supabase.from("Student").insert(studs).select("student_id"); if (error) throw error; return data!.map((s) => s.student_id); } @@ -62,10 +52,7 @@ async function seedInternships() { internship_period: faker.number.int({ min: 4, max: 24 }), internship_year: faker.helpers.arrayElement(["1A", "2A", "3A"]), })); - const { data, error } = await supabase - .from("Internship") - .insert(interns) - .select("internship_id"); + const { data, error } = await supabase.from("Internship").insert(interns).select("internship_id"); if (error) throw error; return data!.map((i) => i.internship_id); } @@ -93,7 +80,7 @@ async function main() { const studIds = await seedStudents(orgIds); console.log("→ Création des internships…"); const internIds = await seedInternships(); - console.log(" → Création des liaisons Complete…"); + console.log("→ Création des liaisons Complete…"); await seedComplete(studIds, internIds); console.log("✅ Seeding terminé !"); } From 03683d9b22f3601de4030d04c4fecbf731f410e7 Mon Sep 17 00:00:00 2001 From: ItsMii176 Date: Mon, 30 Jun 2025 10:40:32 +0200 Subject: [PATCH 3/7] =?UTF-8?q?=E2=9C=A8=20Refactor=20internships=20to=20u?= =?UTF-8?q?se=20database=20and=20API=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces usage of static fake data with dynamic data fetched from a new /api/internships endpoint backed by a PostgreSQL database via Drizzle ORM. Adds database schema, query utilities, data converters, and React hooks for fetching and displaying internships. Updates UI components to use new types and data flow, adds skeleton loaders, and improves error handling and sorting. Cleans up seed scripts and removes unnecessary console logs. --- app/api/internships/route.ts | 29 +++++ app/not-found.tsx | 42 +++++++ app/page.tsx | 2 +- backend/src/seed.ts | 16 --- src/components/ui/skeleton.tsx | 13 ++ src/features/internship/internship-card.tsx | 36 +++--- src/features/internship/internship-filter.tsx | 6 +- src/features/internship/internship-header.tsx | 8 +- .../internship/internship-list-client.tsx | 51 +++++--- .../internship/internship-skeleton.tsx | 70 +++++++++++ src/features/internship/internship-sort.tsx | 4 +- src/hooks/use-internships.ts | 52 ++++++++ src/lib/data-converters.ts | 92 +++++++++++++++ src/lib/db/index.ts | 16 +++ src/lib/db/queries.ts | 111 ++++++++++++++++++ src/lib/db/schema.ts | 57 +++++++++ src/lib/format-date.ts | 22 +++- src/tools/seed.ts | 4 - src/types/database.ts | 91 ++++++++++++++ 19 files changed, 651 insertions(+), 71 deletions(-) create mode 100644 app/api/internships/route.ts create mode 100644 app/not-found.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/features/internship/internship-skeleton.tsx create mode 100644 src/hooks/use-internships.ts create mode 100644 src/lib/data-converters.ts create mode 100644 src/lib/db/index.ts create mode 100644 src/lib/db/queries.ts create mode 100644 src/lib/db/schema.ts create mode 100644 src/types/database.ts diff --git a/app/api/internships/route.ts b/app/api/internships/route.ts new file mode 100644 index 0000000..3feeb96 --- /dev/null +++ b/app/api/internships/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from "next/server"; +import { getAllInternshipsData, transformToCardData } from "@/lib/db/queries"; + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const sort = searchParams.get('sort') || 'most-recent'; + + const data = await getAllInternshipsData(sort); + const transformedData = transformToCardData(data); + + return NextResponse.json({ + success: true, + data: transformedData, + count: transformedData.length + }); + } catch (error) { + console.error("❌ Erreur lors de la récupération des stages:", error); + + return NextResponse.json( + { + success: false, + error: "Impossible de récupérer les données des stages. Veuillez vérifier la connexion à la base de données.", + details: error instanceof Error ? error.message : "Erreur inconnue" + }, + { status: 500 } + ); + } +} diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..d29126e --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,42 @@ +"use client"; +import { AlertTriangle, RefreshCw } from "lucide-react"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; + +export default function NotFound() { + return ( +
+ + +
+ +
+ Erreur 500 + + Connexion à la base de données impossible + +
+ + + + + Solutions possibles + +
    +
  • • Vérifiez votre connexion internet
  • +
  • • Actualisez la page dans quelques minutes
  • +
  • • Contactez l'administrateur si le problème persiste
  • +
+
+
+ + +
+
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index ffd508a..e87c9aa 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,7 +3,7 @@ import { InternshipListClient } from "../src/features/internship/internship-list export default async function RoutePage(_: PageParams) { return ( -
+
); diff --git a/backend/src/seed.ts b/backend/src/seed.ts index a116c2a..bb534e9 100644 --- a/backend/src/seed.ts +++ b/backend/src/seed.ts @@ -25,21 +25,12 @@ const N_STUD = 8; const N_COMPLETE = 8; async function cleanDatabase() { - console.log("🧹 Nettoyage de la base de données..."); - try { // Supprime dans l'ordre inverse des contraintes (relations d'abord) await db.delete(completeTable); - console.log(" → Table Complete vidée"); - await db.delete(internshipsTable); - console.log(" → Table Internship vidée"); - await db.delete(studentsTable); - console.log(" → Table Student vidée"); - await db.delete(organizationsTable); - console.log(" → Table Organization vidée"); console.log("✅ Base de données nettoyée avec succès !"); } catch (error) { @@ -49,8 +40,6 @@ async function cleanDatabase() { } async function seedOrganizations() { - console.log("→ Création des organisations..."); - const orgs = Array.from({ length: N_ORG }).map(() => ({ organization_name: faker.company.name(), tutor_firstname: faker.person.firstName(), @@ -70,8 +59,6 @@ async function seedOrganizations() { } async function seedStudents(orgIds: number[]) { - console.log("→ Création des étudiants..."); - const students = Array.from({ length: N_STUD }).map(() => ({ student_firstname: faker.person.firstName(), student_lastname: faker.person.lastName(), @@ -99,8 +86,6 @@ async function seedStudents(orgIds: number[]) { } async function seedComplete(studentIds: number[]) { - console.log("→ Création des relations étudiant-stage indépendantes..."); - const completes = []; let lastInternshipId = 0; @@ -141,7 +126,6 @@ async function seedComplete(studentIds: number[]) { } await db.insert(completeTable).values(completes); - console.log(` → ${completes.length} relations étudiant-stage indépendantes créées`); return lastInternshipId; } diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx new file mode 100644 index 0000000..32ea0ef --- /dev/null +++ b/src/components/ui/skeleton.tsx @@ -0,0 +1,13 @@ +import { cn } from "@/lib/utils" + +function Skeleton({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Skeleton } diff --git a/src/features/internship/internship-card.tsx b/src/features/internship/internship-card.tsx index 29e5db3..17cd1b0 100644 --- a/src/features/internship/internship-card.tsx +++ b/src/features/internship/internship-card.tsx @@ -1,3 +1,4 @@ +import type { InternshipCardData } from "@/types/database"; import { Building, Calendar, Clock, GraduationCap, MapPin } from "lucide-react"; import { Button } from "@/components/ui/button"; import { @@ -8,50 +9,47 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import type { InternshipFD, OrganizationFD, StudentFD } from "@/data/fake-data"; import { formatDate } from "@/lib/format-date"; type InternshipCardProps = { - internship: InternshipFD; - organization: OrganizationFD; - student: StudentFD; + internship: InternshipCardData; }; -export function InternshipCard({ internship, organization, student }: InternshipCardProps) { +export function InternshipCard({ internship }: InternshipCardProps) { return (
- {organization.orgName} + {internship.organization.orgName} - - {organization.orgType} + + {internship.organization.orgType} - -

{internship.subject}

-
+ +

{internship.internship.subject}

+
- + - {organization.country}, {organization.city} + {internship.organization.country}, {internship.organization.city}
- + - {internship.year} - {student.major} + {internship.internship.year} - {internship.student.major}
- - {internship.weeksCount} semaines + + {internship.internship.weeksCount} semaines
- - {formatDate(internship.date)} + + {formatDate(internship.internship.date)}
diff --git a/src/features/internship/internship-filter.tsx b/src/features/internship/internship-filter.tsx index e307f05..4dd3ce5 100644 --- a/src/features/internship/internship-filter.tsx +++ b/src/features/internship/internship-filter.tsx @@ -1,9 +1,5 @@ "use client"; export function InternshipFilter() { - return ( -
-

Filtres :

-
- ); + return
; } diff --git a/src/features/internship/internship-header.tsx b/src/features/internship/internship-header.tsx index c44dc7b..a7cc8a5 100644 --- a/src/features/internship/internship-header.tsx +++ b/src/features/internship/internship-header.tsx @@ -1,12 +1,12 @@ "use client"; -import type { InternshipFD } from "@/data/fake-data"; +import type { InternshipCardData } from "@/types/database"; import { InternshipFilter } from "@/features/internship/internship-filter"; import { SortInternshipButton } from "@/features/internship/internship-sort"; type InternshipHeaderProps = { sort: string; setSort: (v: string) => void; - fakeInternships: InternshipFD[]; + fakeInternships: InternshipCardData[]; }; export function InternshipHeader({ fakeInternships, sort, setSort }: InternshipHeaderProps) { @@ -15,13 +15,13 @@ export function InternshipHeader({ fakeInternships, sort, setSort }: InternshipH
-
+

{fakeInternships.length} stage{fakeInternships.length > 1 ? "s" : ""} trouvé {fakeInternships.length > 1 ? "s" : ""}

-

Trier par :

+

Trier par :

diff --git a/src/features/internship/internship-list-client.tsx b/src/features/internship/internship-list-client.tsx index a9bdb99..a59557e 100644 --- a/src/features/internship/internship-list-client.tsx +++ b/src/features/internship/internship-list-client.tsx @@ -1,30 +1,45 @@ "use client"; +import { Search } from "lucide-react"; +import { notFound } from "next/navigation"; import { useQueryState } from "nuqs"; -import { fakeInternships, fakeOrganizations, fakeStudents } from "@/data/fake-data"; +import { Alert, AlertTitle } from "@/components/ui/alert"; import { InternshipCard } from "@/features/internship/internship-card"; import { InternshipHeader } from "@/features/internship/internship-header"; -import { getSortedInternships } from "@/features/internship/sort-internship"; +import { InternshipListSkeleton } from "@/features/internship/internship-skeleton"; +import { useInternships } from "@/hooks/use-internships"; export function InternshipListClient() { const [sort, setSort] = useQueryState("sort", { defaultValue: "most-recent" }); - const sortedData = getSortedInternships(sort, fakeInternships, fakeOrganizations); + const { internships, loading, error } = useInternships(sort); + + if (loading) { + return ; + } + + if (error) { + notFound(); + } + + if (internships.length === 0) { + return ( +
+ +
+ + + Aucun stage trouvé + +
+
+ ); + } return ( -
- - {sortedData.map((elem) => { - const internship = fakeInternships[elem.id - 1]; - const org = fakeOrganizations[elem.id - 1]; - const student = fakeStudents[elem.id - 1]; - return ( - - ); - })} +
+ + {internships.map((internship) => ( + + ))}
); } diff --git a/src/features/internship/internship-skeleton.tsx b/src/features/internship/internship-skeleton.tsx new file mode 100644 index 0000000..0125ffa --- /dev/null +++ b/src/features/internship/internship-skeleton.tsx @@ -0,0 +1,70 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export function InternshipCardSkeleton() { + return ( +
+ {/* Header */} +
+ +
+ + +
+
+ + {/* Content */} +
+
+ + + +
+ + {/* Grid info */} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + {/* Footer */} +
+ +
+
+ ); +} + +export function InternshipListSkeleton() { + return ( +
+ {/* Count and sort skeleton */} +
+ +
+ + +
+
+ + {/* Cards skeleton */} + + + +
+ ); +} diff --git a/src/features/internship/internship-sort.tsx b/src/features/internship/internship-sort.tsx index b09fed2..f1a4818 100644 --- a/src/features/internship/internship-sort.tsx +++ b/src/features/internship/internship-sort.tsx @@ -24,9 +24,9 @@ export function SortInternshipButton({ Trier par : Date de début - Nom - Lieu + Nom Durée + Lieu diff --git a/src/hooks/use-internships.ts b/src/hooks/use-internships.ts new file mode 100644 index 0000000..5181b96 --- /dev/null +++ b/src/hooks/use-internships.ts @@ -0,0 +1,52 @@ +import type { InternshipCardData } from "@/types/database"; +import { useCallback, useEffect, useState } from "react"; + +interface APIResponse { + success: boolean; + data?: InternshipCardData[]; + count?: number; + error?: string; + details?: string; +} + +export function useInternships(sort: string = "most-recent") { + const [internships, setInternships] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchInternships = useCallback(async () => { + try { + setLoading(true); + setError(null); + + const url = new URL("/api/internships", window.location.origin); + url.searchParams.set("sort", sort); + + const response = await fetch(url.toString()); + + if (!response.ok) { + throw new Error(`Erreur HTTP: ${response.status}`); + } + + const result: APIResponse = await response.json(); + + if (result.success && result.data) { + setInternships(result.data); + } else { + setError(result.error || "Erreur lors de la récupération des données"); + } + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Erreur inconnue"; + setError(errorMessage); + console.error("Erreur lors de la récupération des stages:", err); + } finally { + setLoading(false); + } + }, [sort]); + + useEffect(() => { + fetchInternships(); + }, [fetchInternships]); + + return { internships, loading, error, refetch: fetchInternships }; +} diff --git a/src/lib/data-converters.ts b/src/lib/data-converters.ts new file mode 100644 index 0000000..71de75d --- /dev/null +++ b/src/lib/data-converters.ts @@ -0,0 +1,92 @@ +import type { + ParsedInternshipData, + InternshipCardData, + ParsedInternship, + ParsedStudent, + ParsedOrganization +} from "@/types/database"; + +/** + * Utilitaires pour convertir entre les formats projetParseur et database + */ + +/** + * Convertit les données du projetParseur vers le format InternshipCardData + */ +export function convertParsedToCardData( + parsed: ParsedInternshipData & { id: number } +): InternshipCardData { + return { + id: parsed.id, + internship: { + subject: parsed.internship.subject, + confidential: typeof parsed.internship.confidential === 'string' + ? parsed.internship.confidential.toLowerCase() === 'true' || parsed.internship.confidential === 'Oui' + : parsed.internship.confidential, + date: parsed.internship.date, + weeksCount: typeof parsed.internship.weeksCount === 'string' + ? parseInt(parsed.internship.weeksCount) + : parsed.internship.weeksCount, + year: parsed.internship.year, + }, + student: { + firstName: parsed.student.firstName, + lastName: parsed.student.lastName, + major: parsed.student.major, + }, + organization: { + orgName: parsed.organization.orgName, + tutorFirstName: parsed.organization.tutorFirstName, + tutorLastName: parsed.organization.tutorLastName, + orgType: parsed.organization.orgType === "E" ? "Entreprise" : "Laboratoire", + country: parsed.organization.country, + city: undefined, // Non disponible dans projetParseur + }, + }; +} + +/** + * Convertit InternshipCardData vers le format projetParseur + */ +export function convertCardDataToParsed( + cardData: InternshipCardData +): ParsedInternshipData { + return { + internship: { + subject: cardData.internship.subject, + confidential: cardData.internship.confidential, + date: cardData.internship.date, + weeksCount: cardData.internship.weeksCount, + year: cardData.internship.year, + }, + student: { + firstName: cardData.student.firstName, + lastName: cardData.student.lastName, + major: cardData.student.major, + }, + organization: { + orgName: cardData.organization.orgName, + tutorFirstName: cardData.organization.tutorFirstName, + tutorLastName: cardData.organization.tutorLastName, + orgType: cardData.organization.orgType === "Entreprise" ? "E" : "L", + country: cardData.organization.country, + }, + }; +} + +/** + * Valide qu'un objet a la structure ParsedInternshipData + */ +export function isParsedInternshipData(obj: any): obj is ParsedInternshipData { + return ( + obj && + typeof obj === 'object' && + obj.internship && + obj.student && + obj.organization && + typeof obj.internship.subject === 'string' && + typeof obj.student.firstName === 'string' && + typeof obj.student.lastName === 'string' && + typeof obj.organization.orgName === 'string' + ); +} diff --git a/src/lib/db/index.ts b/src/lib/db/index.ts new file mode 100644 index 0000000..6318039 --- /dev/null +++ b/src/lib/db/index.ts @@ -0,0 +1,16 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; + +// Configuration de la base de données pour Next.js +const connectionString = process.env.DATABASE_URL || "postgresql://postgres:password@localhost:5432/chronos_nova"; + +// Création du client postgres +const client = postgres(connectionString, { + max: 1, // Limite les connexions pour Next.js +}); + +// Création de l'instance Drizzle +export const db = drizzle(client); + +// Export du client pour pouvoir fermer la connexion si nécessaire +export { client }; diff --git a/src/lib/db/queries.ts b/src/lib/db/queries.ts new file mode 100644 index 0000000..c11adf8 --- /dev/null +++ b/src/lib/db/queries.ts @@ -0,0 +1,111 @@ +import { eq, asc, desc, sql } from "drizzle-orm"; +import { db } from "./index"; +import { completeTable, internshipsTable, organizationsTable, studentsTable } from "./schema"; +import type { InternshipCardData } from "@/types/database"; + +/** + * Fonction de base pour construire la requête avec jointures + * Évite la duplication de code entre getAllInternshipsData et getInternshipData + */ +function createBaseInternshipQuery() { + return db + .select({ + internship: internshipsTable, + student: studentsTable, + organization: organizationsTable, + }) + .from(completeTable) + .innerJoin(internshipsTable, eq(completeTable.internship_id, internshipsTable.internship_id)) + .innerJoin(studentsTable, eq(completeTable.student_id, studentsTable.student_id)) + .innerJoin( + organizationsTable, + eq(studentsTable.organization_id, organizationsTable.organization_id), + ); +} + +/** + * Récupère tous les stages avec leurs étudiants et organisations associés + * @param sort - Type de tri à appliquer ('most-recent', 'name', 'duration', 'location') + */ +export async function getAllInternshipsData(sort: string = 'most-recent') { + try { + const baseQuery = createBaseInternshipQuery(); + + // Application du tri selon le paramètre + switch (sort) { + case 'most-recent': + // Conversion des dates françaises DD/MM/YYYY en format PostgreSQL pour tri + return await baseQuery.orderBy( + desc(sql`TO_DATE(${internshipsTable.internship_dates}, 'DD/MM/YYYY')`) + ); + case 'organization': + return await baseQuery.orderBy(asc(organizationsTable.organization_name)); + case 'duration': + // Tri par durée de stage (période en semaines) - ordre croissant (plus court d'abord) + return await baseQuery.orderBy(asc(internshipsTable.internship_period)); + case 'location': + // Tri par localisation : pays puis ville (ordre alphabétique) + return await baseQuery.orderBy( + asc(organizationsTable.organization_country), + asc(organizationsTable.organization_city) + ); + default: + // Tri par défaut : plus récent + return await baseQuery.orderBy( + desc(sql`TO_DATE(${internshipsTable.internship_dates}, 'DD/MM/YYYY')`) + ); + } + } catch (error) { + console.error("❌ Erreur lors de la récupération des données de stages:", error); + throw error; // Re-throw pour que l'API puisse gérer l'erreur + } +} + +/** + * Récupère un stage spécifique avec tous ses étudiants et organisation + * @param internshipId - ID du stage à récupérer + */ +export async function getInternshipData(internshipId: number) { + try { + const data = await createBaseInternshipQuery() + .where(eq(completeTable.internship_id, internshipId)); + + return data; + } catch (error) { + console.error(`❌ Erreur lors de la récupération du stage ${internshipId}:`, error); + throw error; + } +} + +/** + * Transforme les données de la base en format compatible avec les composants frontend + */ +export function transformToCardData(data: Array<{ + internship: typeof internshipsTable.$inferSelect; + student: typeof studentsTable.$inferSelect; + organization: typeof organizationsTable.$inferSelect; +}>): InternshipCardData[] { + return data.map((item) => ({ + id: item.internship.internship_id, + internship: { + subject: item.internship.internship_subject, + confidential: item.internship.internship_confidential, + date: item.internship.internship_dates, + weeksCount: item.internship.internship_period, + year: item.internship.internship_year, + }, + student: { + firstName: item.student.student_firstname, + lastName: item.student.student_lastname, + major: item.student.student_degree || undefined, + }, + organization: { + orgName: item.organization.organization_name, + tutorFirstName: item.organization.tutor_firstname, + tutorLastName: item.organization.tutor_lastname, + orgType: item.organization.organization_type === "E" ? "Entreprise" : "Laboratoire", + country: item.organization.organization_country || undefined, + city: item.organization.organization_city || undefined, + }, + })); +} diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts new file mode 100644 index 0000000..276ba1f --- /dev/null +++ b/src/lib/db/schema.ts @@ -0,0 +1,57 @@ +import { boolean, foreignKey, integer, pgTable, serial, text } from "drizzle-orm/pg-core"; + +export const organizationsTable = pgTable("Organization", { + organization_id: serial("organization_id").primaryKey(), + organization_name: text("organization_name").notNull(), + tutor_firstname: text("tutor_firstname").notNull(), + tutor_lastname: text("tutor_lastname").notNull(), + organization_type: text("organization_type").notNull(), + organization_country: text("organization_country").notNull(), + organization_city: text("organization_city").notNull(), + organization_postal_code: integer("organization_postal_code").notNull(), +}); + +export const studentsTable = pgTable( + "Student", + { + student_id: serial("student_id").primaryKey(), + student_firstname: text("student_firstname").notNull(), + student_lastname: text("student_lastname").notNull(), + student_degree: text("student_degree").notNull(), + student_course: text("student_course").notNull(), + organization_id: integer("organization_id").notNull(), + }, + (table) => ({ + orgFK: foreignKey({ + columns: [table.organization_id], + foreignColumns: [organizationsTable.organization_id], + }), + }), +); + +export const internshipsTable = pgTable("Internship", { + internship_id: serial("internship_id").primaryKey(), + internship_subject: text("internship_subject").notNull(), + internship_confidential: boolean("internship_confidential").notNull(), + internship_dates: text("internship_dates").notNull(), + internship_period: integer("internship_period").notNull(), + internship_year: text("internship_year").notNull(), +}); + +export const completeTable = pgTable( + "Complete", + { + internship_id: integer("internship_id").notNull(), + student_id: integer("student_id").notNull(), + }, + (table) => ({ + internshipFK: foreignKey({ + columns: [table.internship_id], + foreignColumns: [internshipsTable.internship_id], + }), + studentFK: foreignKey({ + columns: [table.student_id], + foreignColumns: [studentsTable.student_id], + }), + }), +); diff --git a/src/lib/format-date.ts b/src/lib/format-date.ts index 76aad4f..e44611a 100644 --- a/src/lib/format-date.ts +++ b/src/lib/format-date.ts @@ -1,8 +1,26 @@ export function formatDate(dateStr: string) { - const date = new Date(dateStr); + // Gestion des différents formats de date + let date: Date; + + // Si c'est au format français dd/MM/yyyy (de la base de données) + if (dateStr.includes('/') && dateStr.split('/').length === 3) { + const [day, month, year] = dateStr.split('/').map(Number); + date = new Date(year, month - 1, day); // month - 1 car les mois sont indexés à partir de 0 + } + // Si c'est au format ISO ou autre format standard + else { + date = new Date(dateStr); + } + + // Vérification que la date est valide + if (isNaN(date.getTime())) { + console.warn(`Date invalide: ${dateStr}`); + return dateStr; // Retourne la chaîne originale si elle ne peut pas être parsée + } + return date.toLocaleDateString("fr-FR", { day: "numeric", - month: "long", + month: "long", year: "numeric", }); } diff --git a/src/tools/seed.ts b/src/tools/seed.ts index 093f784..2b25571 100644 --- a/src/tools/seed.ts +++ b/src/tools/seed.ts @@ -74,13 +74,9 @@ async function seedComplete(studentIds: number[], internshipIds: number[]) { } async function main() { - console.log("→ Création des organisations…"); const orgIds = await seedOrganizations(); - console.log("→ Création des étudiants…"); const studIds = await seedStudents(orgIds); - console.log("→ Création des internships…"); const internIds = await seedInternships(); - console.log("→ Création des liaisons Complete…"); await seedComplete(studIds, internIds); console.log("✅ Seeding terminé !"); } diff --git a/src/types/database.ts b/src/types/database.ts new file mode 100644 index 0000000..a937f8f --- /dev/null +++ b/src/types/database.ts @@ -0,0 +1,91 @@ +// Types basés sur le schéma de base de données +export interface Internship { + internship_id: number; + internship_subject: string; + internship_confidential: boolean; + internship_dates: string; + internship_period: number; + internship_year: string; +} + +export interface Student { + student_id: number; + student_firstname: string; + student_lastname: string; + student_degree: string; + student_course: string; + organization_id: number; +} + +export interface Organization { + organization_id: number; + organization_name: string; + tutor_firstname: string; + tutor_lastname: string; + organization_type: string; + organization_country: string; + organization_city: string; + organization_postal_code: number; +} + +// Type pour les données complètes d'un stage avec ses relations +export interface CompleteInternshipData { + internship: Internship; + student: Student; + organization: Organization; +} + +// Type pour les props des composants (compatible avec typeDefinition du projetParseur) +export interface InternshipCardData { + id: number; + internship: { + subject: string; + confidential: boolean; + date: string; + weeksCount: number; + year: string; + }; + student: { + firstName: string; + lastName: string; + major?: string; + }; + organization: { + orgName: string; + tutorFirstName: string; + tutorLastName: string; + orgType: string; + country?: string; + city?: string; + }; +} + +// Types compatibles avec le projetParseur (pour import/export de données) +export interface ParsedInternship { + subject: string; + confidential: string | boolean; + date: string; + weeksCount: number | string; + year: string; +} + +export interface ParsedStudent { + lastName: string; + firstName: string; + major?: string; +} + +export interface ParsedOrganization { + orgName: string; + tutorLastName: string; + tutorFirstName: string; + orgType: string; + country?: string; +} + +// Type combiné pour import depuis projetParseur +export interface ParsedInternshipData { + internship: ParsedInternship; + student: ParsedStudent; + organization: ParsedOrganization; +} From f3cb93bddf8a147d0864a8ff08755710d42c5e64 Mon Sep 17 00:00:00 2001 From: ItsMii176 Date: Tue, 1 Jul 2025 18:42:24 +0200 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=94=A7=20Parser-DB=20integration=20wi?= =?UTF-8?q?th=20enhanced=20error=20handling=20and=20field=20normalization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Connected the parser to the database, enabling real-time data retrieval - Implemented HTTP 500 error response when a database read operation fails - Added a clear error message when no internship/stage entries are found - Improved normalization logic across various fields to ensure consistent formatting and cleaner data input/output --- app/layout.tsx | 4 +- app/page.tsx | 2 +- backend/projetParseur/package.json | 12 +- backend/projetParseur/pnpm-lock.yaml | 1230 ++++++++++------- backend/projetParseur/src/data-normalizer.ts | 162 +++ backend/projetParseur/src/excel-validator.ts | 378 +++++ backend/projetParseur/src/functions.ts | 108 +- backend/projetParseur/src/index.ts | 58 +- .../projetParseur/src/inserter-supabase.ts | 88 ++ ...nternship2A.ts => parser-internship-2A.ts} | 51 +- ...p.ts => parser-substitution-internship.ts} | 44 +- backend/projetParseur/src/parser.ts | 168 +++ backend/projetParseur/src/type-definition.ts | 27 + backend/projetParseur/src/typeDefinition.ts | 26 - backend/projetParseur/tsconfig.json | 124 +- scripts/update-org-types.cjs | 62 + scripts/update-org-types.ts | 56 + src/features/internship/internship-card.tsx | 8 +- .../internship/internship-list-client.tsx | 16 +- src/lib/data-converters.ts | 1 + src/lib/db/queries.ts | 53 +- src/lib/format-date.ts | 18 +- src/types/database.ts | 2 + 23 files changed, 1879 insertions(+), 819 deletions(-) create mode 100644 backend/projetParseur/src/data-normalizer.ts create mode 100644 backend/projetParseur/src/excel-validator.ts create mode 100644 backend/projetParseur/src/inserter-supabase.ts rename backend/projetParseur/src/{parserInternship2A.ts => parser-internship-2A.ts} (59%) rename backend/projetParseur/src/{parserSubstitutionInternship.ts => parser-substitution-internship.ts} (61%) create mode 100644 backend/projetParseur/src/parser.ts create mode 100644 backend/projetParseur/src/type-definition.ts delete mode 100644 backend/projetParseur/src/typeDefinition.ts create mode 100644 scripts/update-org-types.cjs create mode 100644 scripts/update-org-types.ts diff --git a/app/layout.tsx b/app/layout.tsx index 237c590..e98113e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -13,10 +13,10 @@ export const metadata: Metadata = { export default function RootLayout({ children }: LayoutParams) { return ( - + -
+
{children}