From d70b3061b572b82d706233760ba32c0832d433a9 Mon Sep 17 00:00:00 2001 From: Bradley Shellnut Date: Sun, 21 Jul 2024 12:05:48 -0700 Subject: [PATCH] Moving a lot around for hono --- package.json | 7 + pnpm-lock.yaml | 637 +++++++++++++++++- src/app.d.ts | 21 +- src/db/schema/categoriesToExternalIds.ts | 15 + src/db/schema/collections.ts | 8 +- src/db/schema/index.ts | 4 +- src/db/schema/passwordResetTokens.ts | 8 +- src/db/schema/recoveryCodes.ts | 4 +- .../schema/{sessions.ts => sessions.table.ts} | 19 +- src/db/schema/two-factor.table.ts | 8 +- src/db/schema/userRoles.ts | 8 +- src/db/schema/{users.ts => users.table.ts} | 8 +- src/db/schema/wishlists.ts | 8 +- src/hooks.server.ts | 40 +- src/lib/server/api/common/config.ts | 14 + src/lib/server/api/common/errors.ts | 26 + .../server/api/controllers/iam.controller.ts | 10 + src/lib/server/api/index.ts | 23 + .../server/api/infrastructure/auth/lucia.ts | 56 ++ .../server/api/middleware/auth.middleware.ts | 50 ++ .../api/middleware/rate-limiter.middleware.ts | 32 + src/lib/server/api/types/index.ts | 14 + src/lib/server/auth.ts | 4 +- src/lib/utils/api.ts | 25 + src/routes/api/[...slug]/+server.ts | 9 + 25 files changed, 999 insertions(+), 59 deletions(-) rename src/db/schema/{sessions.ts => sessions.table.ts} (50%) rename src/db/schema/{users.ts => users.table.ts} (79%) create mode 100644 src/lib/server/api/common/config.ts create mode 100644 src/lib/server/api/common/errors.ts create mode 100644 src/lib/server/api/controllers/iam.controller.ts create mode 100644 src/lib/server/api/index.ts create mode 100644 src/lib/server/api/infrastructure/auth/lucia.ts create mode 100644 src/lib/server/api/middleware/auth.middleware.ts create mode 100644 src/lib/server/api/middleware/rate-limiter.middleware.ts create mode 100644 src/lib/server/api/types/index.ts create mode 100644 src/lib/utils/api.ts create mode 100644 src/routes/api/[...slug]/+server.ts diff --git a/package.json b/package.json index 7ee40bd..63fa3e3 100644 --- a/package.json +++ b/package.json @@ -77,8 +77,10 @@ }, "dependencies": { "@fontsource/fira-mono": "^5.0.13", + "@hono/zod-validator": "^0.2.2", "@iconify-icons/line-md": "^1.2.30", "@iconify-icons/mdi": "^1.2.48", + "@internationalized/date": "^3.5.4", "@lucia-auth/adapter-drizzle": "^1.0.7", "@lukeed/uuid": "^2.0.1", "@neondatabase/serverless": "^0.9.4", @@ -97,8 +99,11 @@ "drizzle-orm": "^0.32.0", "feather-icons": "^4.29.2", "formsnap": "^1.0.1", + "hono": "^4.5.0", + "hono-rate-limiter": "^0.4.0", "html-entities": "^2.5.2", "iconify-icon": "^2.1.0", + "ioredis": "^5.4.1", "just-capitalize": "^3.2.0", "just-kebab-case": "^4.2.0", "loader": "^2.1.1", @@ -110,6 +115,8 @@ "postgres": "^3.4.4", "qrcode": "^1.5.3", "radix-svelte": "^0.9.0", + "rate-limit-redis": "^4.2.0", + "reflect-metadata": "^0.2.2", "svelte-french-toast": "^1.2.0", "svelte-lazy-loader": "^1.0.0", "tailwind-merge": "^2.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8d3c65..d950581 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,12 +11,18 @@ importers: '@fontsource/fira-mono': specifier: ^5.0.13 version: 5.0.13 + '@hono/zod-validator': + specifier: ^0.2.2 + version: 0.2.2(hono@4.5.0)(zod@3.23.8) '@iconify-icons/line-md': specifier: ^1.2.30 version: 1.2.30 '@iconify-icons/mdi': specifier: ^1.2.48 version: 1.2.48 + '@internationalized/date': + specifier: ^3.5.4 + version: 3.5.4 '@lucia-auth/adapter-drizzle': specifier: ^1.0.7 version: 1.0.7(lucia@3.2.0) @@ -71,12 +77,21 @@ importers: formsnap: specifier: ^1.0.1 version: 1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.16.1(@sveltejs/kit@2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.175)(vite@5.3.4(@types/node@20.14.11)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.3.4(@types/node@20.14.11)(sass@1.77.8)))(svelte@5.0.0-next.175)) + hono: + specifier: ^4.5.0 + version: 4.5.0 + hono-rate-limiter: + specifier: ^0.4.0 + version: 0.4.0(hono@4.5.0) html-entities: specifier: ^2.5.2 version: 2.5.2 iconify-icon: specifier: ^2.1.0 version: 2.1.0 + ioredis: + specifier: ^5.4.1 + version: 5.4.1 just-capitalize: specifier: ^3.2.0 version: 3.2.0 @@ -110,6 +125,12 @@ importers: radix-svelte: specifier: ^0.9.0 version: 0.9.0(svelte@5.0.0-next.175) + rate-limit-redis: + specifier: ^4.2.0 + version: 4.2.0(express-rate-limit@7.3.1(express@4.19.2)) + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 svelte-french-toast: specifier: ^1.2.0 version: 1.2.0(svelte@5.0.0-next.175) @@ -1154,6 +1175,12 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@hono/zod-validator@0.2.2': + resolution: {integrity: sha512-dSDxaPV70Py8wuIU2QNpoVEIOSzSXZ/6/B/h4xA7eOMz7+AarKTSGV8E6QwrdcCbBLkpqfJ4Q2TmBO0eP1tCBQ==} + peerDependencies: + hono: '>=3.9.0' + zod: ^3.19.1 + '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -1287,12 +1314,12 @@ packages: cpu: [x64] os: [win32] - '@internationalized/date@3.5.3': - resolution: {integrity: sha512-X9bi8NAEHAjD8yzmPYT2pdJsbe+tYSEBAfowtlxJVJdZR3aK8Vg7ZUT1Fm5M47KLzp/M1p1VwAaeSma3RT7biw==} - '@internationalized/date@3.5.4': resolution: {integrity: sha512-qoVJVro+O0rBaw+8HPjUB1iH8Ihf8oziEnqMnvhJUSuVIrHOuZ6eNLHNvzXJKUvAtaDiqMnRlg8Z2mgh09BlUw==} + '@ioredis/commands@1.2.0': + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2022,6 +2049,10 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -2111,6 +2142,9 @@ packages: arktype@2.0.0-beta.0: resolution: {integrity: sha512-fE3ssMiXjr/bLqFPzlDhRlXngdyHQreu7p7i8+dtcY1CA+f8WrVUcue6JxywhnqEJXPG4HOcIwQcC+q4VfeUMQ==} + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -2154,6 +2188,10 @@ packages: resolution: {integrity: sha512-sSL27GGYHBp7PEt/j3NyrCpkMWgtu0I0VxYveveU5l5POnBhlSlyESy3rJe/qvHXx55DSE3VuDNeDzHHuIjLHA==} engines: {node: '>=10'} + body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -2184,10 +2222,18 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2249,6 +2295,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + code-red@1.0.4: resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} @@ -2283,6 +2333,17 @@ packages: console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} @@ -2343,6 +2404,14 @@ packages: dayjs@1.11.11: resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -2376,13 +2445,29 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -2523,6 +2608,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.4.758: resolution: {integrity: sha512-/o9x6TCdrYZBMdGeTifAP3wlF/gVT+TtWJe3BSmtNh92Mw81U9hrYwW9OAGUh+sEOX/yz5e34sksqRruZbjYrw==} @@ -2541,6 +2629,18 @@ packages: encode-utf8@1.0.3: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} @@ -2653,10 +2753,24 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + express-rate-limit@7.3.1: + resolution: {integrity: sha512-BbaryvkY4wEgDqLgD18/NSy2lDO2jTuT9Y8c1Mpx0X63Yz0sYd5zN6KPe7UvpuSVvV33T6RaE1o1IVZQjHMYgw==} + engines: {node: '>= 16'} + peerDependencies: + express: 4 || 5 || ^5.0.0-beta.1 + + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2698,6 +2812,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -2726,9 +2844,17 @@ packages: svelte: ^4.0.0 || ^5.0.0-next.1 sveltekit-superforms: ^2.3.0 + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -2764,6 +2890,10 @@ packages: get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -2801,6 +2931,9 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -2811,6 +2944,17 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} @@ -2822,9 +2966,22 @@ packages: resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==} engines: {node: '>=6'} + hono-rate-limiter@0.4.0: + resolution: {integrity: sha512-7RWU2HZvxPtfBrvjXKDiQ3F6ZH8k49JhxVkHquUz5UZKjauj5PrP29MvISykThtfpy4mGG6kqxFBHW1ed9wwnA==} + peerDependencies: + hono: ^4.1.1 + + hono@4.5.0: + resolution: {integrity: sha512-ZbezypZfn4odyApjCCv+Fw5OgweBqRLA/EsMyc4FUknFvBJcBIKhHy4sqmD1rWpBc/3wUlaQ6tqOPjk36R1ckg==} + engines: {node: '>=16.0.0'} + html-entities@2.5.2: resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -2836,6 +2993,10 @@ packages: iconify-icon@2.1.0: resolution: {integrity: sha512-lto4XU3bwTQnb+D/CsJ4dWAo0aDe+uPMxEtxyOodw9l7R9QnJUUab3GCehlw2M8mDHdeUu/ufx8PvRQiJphhXg==} + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -2864,6 +3025,14 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ioredis@5.4.1: + resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} + engines: {node: '>=12.22.0'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -3009,6 +3178,12 @@ packages: lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -3040,6 +3215,10 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + memfs-browser@3.5.10302: resolution: {integrity: sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==} @@ -3050,6 +3229,9 @@ packages: memoize-weak@1.0.2: resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==} + merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -3061,6 +3243,10 @@ packages: resolution: {integrity: sha512-D4rT6XIYGqZab0EI/xbihUpwh0WbNRVQ35l2J/5QC2atxaI8h3KvA55DEJLBB/FRdaji7JwkNehfCRjCyjCjqw==} engines: {node: '>=6.0.0'} + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -3069,6 +3255,19 @@ packages: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} + 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'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -3127,9 +3326,15 @@ packages: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -3146,6 +3351,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -3195,9 +3404,17 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -3252,6 +3469,10 @@ packages: parse-css-color@0.2.1: resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3275,6 +3496,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3669,6 +3893,10 @@ packages: property-expr@2.0.6: resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3678,6 +3906,10 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3687,6 +3919,20 @@ packages: peerDependencies: svelte: ^4.1.1 + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rate-limit-redis@4.2.0: + resolution: {integrity: sha512-wV450NQyKC24NmPosJb2131RoczLdfIJdKCReNwtVpm5998U8SgKrAZrIHaN/NfQgqOHaan8Uq++B4sa5REwjA==} + engines: {node: '>= 16'} + peerDependencies: + express-rate-limit: '>= 6' + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3701,9 +3947,20 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + reflect-metadata@0.1.14: resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -3761,6 +4018,9 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sander@0.5.1: resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} @@ -3799,12 +4059,27 @@ packages: engines: {node: '>=10'} hasBin: true + send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + + serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.33.4: resolution: {integrity: sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==} engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3817,6 +4092,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -3860,6 +4139,13 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} @@ -4147,6 +4433,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + toposort@2.0.2: resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} @@ -4214,6 +4504,10 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + typescript@5.5.3: resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} engines: {node: '>=14.17'} @@ -4234,6 +4528,10 @@ packages: unicode-trie@2.0.0: resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + update-browserslist-db@1.0.15: resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==} hasBin: true @@ -4252,6 +4550,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -4265,6 +4567,10 @@ packages: resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} engines: {node: '>= 0.10'} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vite-imagetools@7.0.2: resolution: {integrity: sha512-sA98fIhfIqPdt5qKAMMrQtBzdqK44dlmv4jEUlJMmn4GzR8CdXjKGiyU/GKUFxjReuPi0fK/dd0JhiZixZt06A==} engines: {node: '>=18.0.0'} @@ -5073,6 +5379,11 @@ snapshots: '@hapi/hoek': 9.3.0 optional: true + '@hono/zod-validator@0.2.2(hono@4.5.0)(zod@3.23.8)': + dependencies: + hono: 4.5.0 + zod: 3.23.8 + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -5170,14 +5481,12 @@ snapshots: '@img/sharp-win32-x64@0.33.4': optional: true - '@internationalized/date@3.5.3': - dependencies: - '@swc/helpers': 0.5.11 - '@internationalized/date@3.5.4': dependencies: '@swc/helpers': 0.5.11 + '@ioredis/commands@1.2.0': {} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -5251,7 +5560,7 @@ snapshots: dependencies: '@floating-ui/core': 1.6.2 '@floating-ui/dom': 1.6.5 - '@internationalized/date': 3.5.3 + '@internationalized/date': 3.5.4 dequal: 2.0.3 focus-trap: 7.5.4 nanoid: 5.0.7 @@ -5873,6 +6182,11 @@ snapshots: abbrev@1.1.1: {} + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + acorn-import-attributes@1.9.5(acorn@8.12.0): dependencies: acorn: 8.12.0 @@ -5950,6 +6264,8 @@ snapshots: '@ark/util': 0.1.0 optional: true + array-flatten@1.1.1: {} + array-union@2.1.0: {} assertion-error@1.1.0: {} @@ -5982,7 +6298,7 @@ snapshots: bits-ui@0.21.12(svelte@5.0.0-next.175): dependencies: - '@internationalized/date': 3.5.3 + '@internationalized/date': 3.5.4 '@melt-ui/svelte': 0.76.2(svelte@5.0.0-next.175) nanoid: 5.0.7 svelte: 5.0.0-next.175 @@ -5995,6 +6311,23 @@ snapshots: transitivePeerDependencies: - encoding + body-parser@1.20.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -6030,8 +6363,18 @@ snapshots: buffer-from@1.1.2: {} + bytes@3.1.2: {} + cac@6.7.14: {} + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + callsites@3.1.0: {} camelcase-css@2.0.1: {} @@ -6096,6 +6439,8 @@ snapshots: clsx@2.1.1: {} + cluster-key-slot@1.1.2: {} + code-red@1.0.4: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -6130,6 +6475,14 @@ snapshots: console-control-strings@1.1.0: {} + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.6: {} + cookie@0.6.0: {} core-js@3.37.0: {} @@ -6182,6 +6535,10 @@ snapshots: dayjs@1.11.11: optional: true + debug@2.6.9: + dependencies: + ms: 2.0.0 + debug@4.3.4: dependencies: ms: 2.1.2 @@ -6200,10 +6557,22 @@ snapshots: deepmerge@4.3.1: {} + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + delegates@1.0.0: {} + denque@2.1.0: {} + + depd@2.0.0: {} + dequal@2.0.3: {} + destroy@1.2.0: {} + detect-indent@6.1.0: {} detect-libc@2.0.3: {} @@ -6251,6 +6620,8 @@ snapshots: eastasianwidth@0.2.0: {} + ee-first@1.1.1: {} + electron-to-chromium@1.4.758: {} electron-to-chromium@1.4.818: {} @@ -6263,6 +6634,14 @@ snapshots: encode-utf8@1.0.3: {} + encodeurl@1.0.2: {} + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + es6-promise@3.3.1: {} esbuild-register@3.5.0(esbuild@0.19.12): @@ -6499,6 +6878,8 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 @@ -6511,6 +6892,46 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + express-rate-limit@7.3.1(express@4.19.2): + dependencies: + express: 4.19.2 + + express@4.19.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -6554,6 +6975,18 @@ snapshots: dependencies: to-regex-range: 5.0.1 + finalhandler@1.2.0: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -6587,8 +7020,12 @@ snapshots: svelte: 5.0.0-next.175 sveltekit-superforms: 2.16.1(@sveltejs/kit@2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.175)(vite@5.3.4(@types/node@20.14.11)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.3.4(@types/node@20.14.11)(sass@1.77.8)))(svelte@5.0.0-next.175) + forwarded@0.2.0: {} + fraction.js@4.3.7: {} + fresh@0.5.2: {} + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -6622,6 +7059,14 @@ snapshots: get-func-name@2.0.2: {} + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + get-stream@8.0.1: {} get-tsconfig@4.7.5: @@ -6670,12 +7115,24 @@ snapshots: globrex@0.1.2: {} + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + graceful-fs@4.2.11: {} graphemer@1.4.0: {} has-flag@4.0.0: {} + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + has-unicode@2.0.1: {} hasown@2.0.2: @@ -6684,8 +7141,22 @@ snapshots: hex-rgb@4.3.0: {} + hono-rate-limiter@0.4.0(hono@4.5.0): + dependencies: + hono: 4.5.0 + + hono@4.5.0: {} + html-entities@2.5.2: {} + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 @@ -6699,6 +7170,10 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.1: {} imagetools-core@7.0.0: @@ -6723,6 +7198,22 @@ snapshots: inherits@2.0.4: {} + ioredis@5.4.1: + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.5 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + + ipaddr.js@1.9.1: {} + is-arrayish@0.3.2: {} is-binary-path@2.1.0: @@ -6858,6 +7349,10 @@ snapshots: lodash.clonedeep@4.5.0: {} + lodash.defaults@4.2.0: {} + + lodash.isarguments@3.1.0: {} + lodash.merge@4.6.2: {} loupe@2.3.7: @@ -6886,6 +7381,8 @@ snapshots: mdn-data@2.0.30: {} + media-typer@0.3.0: {} + memfs-browser@3.5.10302: dependencies: memfs: 3.5.3 @@ -6898,12 +7395,16 @@ snapshots: memoize-weak@1.0.2: {} + merge-descriptors@1.0.1: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} meriyah@1.9.15: {} + methods@1.1.2: {} + micromatch@4.0.5: dependencies: braces: 3.0.2 @@ -6914,6 +7415,14 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + mimic-fn@4.0.0: {} min-indent@1.0.1: {} @@ -6962,8 +7471,12 @@ snapshots: mrmime@2.0.0: {} + ms@2.0.0: {} + ms@2.1.2: {} + ms@2.1.3: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -6976,6 +7489,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@0.6.3: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -7010,8 +7525,14 @@ snapshots: object-hash@3.0.0: {} + object-inspect@1.13.2: {} + obuf@1.1.2: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -7074,6 +7595,8 @@ snapshots: color-name: 1.1.4 hex-rgb: 4.3.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -7089,6 +7612,8 @@ snapshots: lru-cache: 10.2.2 minipass: 7.1.2 + path-to-regexp@0.1.7: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -7501,6 +8026,11 @@ snapshots: property-expr@2.0.6: optional: true + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + punycode@2.3.1: {} qrcode@1.5.3: @@ -7510,6 +8040,10 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 + qs@6.11.0: + dependencies: + side-channel: 1.0.6 + queue-microtask@1.2.3: {} radix-svelte@0.9.0(svelte@5.0.0-next.175): @@ -7518,6 +8052,19 @@ snapshots: '@floating-ui/dom': 1.6.5 svelte: 5.0.0-next.175 + range-parser@1.2.1: {} + + rate-limit-redis@4.2.0(express-rate-limit@7.3.1(express@4.19.2)): + dependencies: + express-rate-limit: 7.3.1(express@4.19.2) + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + react-is@18.3.1: {} read-cache@1.0.0: @@ -7534,8 +8081,16 @@ snapshots: dependencies: picomatch: 2.3.1 + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + reflect-metadata@0.1.14: {} + reflect-metadata@0.2.2: {} + regenerator-runtime@0.14.1: optional: true @@ -7620,6 +8175,8 @@ snapshots: safe-buffer@5.2.1: {} + safer-buffer@2.1.2: {} + sander@0.5.1: dependencies: es6-promise: 3.3.1 @@ -7673,10 +8230,48 @@ snapshots: semver@7.6.3: {} + send@0.18.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@1.15.0: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + set-blocking@2.0.0: {} set-cookie-parser@2.6.0: {} + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + setprototypeof@1.2.0: {} + sharp@0.33.4: dependencies: color: 4.2.3 @@ -7709,6 +8304,13 @@ snapshots: shebang-regex@3.0.0: {} + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 + siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -7747,6 +8349,10 @@ snapshots: stackback@0.0.2: {} + standard-as-callback@2.1.0: {} + + statuses@2.0.1: {} + std-env@3.7.0: {} string-width@4.2.3: @@ -8057,6 +8663,8 @@ snapshots: dependencies: is-number: 7.0.0 + toidentifier@1.0.1: {} + toposort@2.0.2: optional: true @@ -8116,6 +8724,11 @@ snapshots: type-fest@2.19.0: optional: true + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + typescript@5.5.3: {} ufo@1.5.3: {} @@ -8131,6 +8744,8 @@ snapshots: pako: 0.2.9 tiny-inflate: 1.0.3 + unpipe@1.0.0: {} + update-browserslist-db@1.0.15(browserslist@4.23.0): dependencies: browserslist: 4.23.0 @@ -8149,6 +8764,8 @@ snapshots: util-deprecate@1.0.2: {} + utils-merge@1.0.1: {} + v8-compile-cache-lib@3.0.1: {} valibot@0.31.1: @@ -8160,6 +8777,8 @@ snapshots: validator@13.12.0: optional: true + vary@1.1.2: {} + vite-imagetools@7.0.2(rollup@4.18.1): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.1) diff --git a/src/app.d.ts b/src/app.d.ts index b8cb412..731a21d 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,3 +1,7 @@ +import { ApiClient } from './lib/server/api'; +import type { User } from 'lucia'; +import { parseApiResponse } from '$lib/utils/api'; + // See https://kit.svelte.dev/docs/types#app // for information about these interfaces // and what to do when importing types @@ -13,6 +17,10 @@ declare global { }; } interface Locals { + api: ApiClient['api']; + parseApiResponse: typeof parseApiResponse; + getAuthedUser: () => Promise | null>; + getAuthedUserOrThrow: () => Promise>; auth: import('lucia').AuthRequest; user: import('lucia').User | null; session: import('lucia').Session | null; @@ -37,18 +45,5 @@ declare global { } } -// interface PageData {} -// interface Error {} -// interface Platform {} - -// /// -// declare global { -// namespace Lucia { -// type Auth = import('$lib/server/lucia').Auth; -// type DatabaseUserAttributes = User; -// type DatabaseSessionAttributes = {}; -// } -// } - // THIS IS IMPORTANT!!! export {}; diff --git a/src/db/schema/categoriesToExternalIds.ts b/src/db/schema/categoriesToExternalIds.ts index e9e865b..352b732 100644 --- a/src/db/schema/categoriesToExternalIds.ts +++ b/src/db/schema/categoriesToExternalIds.ts @@ -1,6 +1,7 @@ import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'; import categories from './categories'; import externalIds from './externalIds'; +import { relations } from 'drizzle-orm'; const categoriesToExternalIds = pgTable( 'categories_to_external_ids', @@ -21,4 +22,18 @@ const categoriesToExternalIds = pgTable( }, ); +export const categoriesToExternalIdsRelations = relations( + categoriesToExternalIds, + ({ one }) => ({ + category: one(categories, { + fields: [categoriesToExternalIds.categoryId], + references: [categories.id], + }), + externalId: one(externalIds, { + fields: [categoriesToExternalIds.externalId], + references: [externalIds.id], + }), + }), +); + export default categoriesToExternalIds; diff --git a/src/db/schema/collections.ts b/src/db/schema/collections.ts index 6ff9916..1a56443 100644 --- a/src/db/schema/collections.ts +++ b/src/db/schema/collections.ts @@ -1,7 +1,7 @@ import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; import { createId as cuid2 } from '@paralleldrive/cuid2'; import { type InferSelectModel, relations } from 'drizzle-orm'; -import users from './users'; +import usersTable from './users.table'; import { timestamps } from '../utils'; const collections = pgTable('collections', { @@ -11,15 +11,15 @@ const collections = pgTable('collections', { .$defaultFn(() => cuid2()), user_id: uuid('user_id') .notNull() - .references(() => users.id, { onDelete: 'cascade' }), + .references(() => usersTable.id, { onDelete: 'cascade' }), name: text('name').notNull().default('My Collection'), ...timestamps, }); export const collection_relations = relations(collections, ({ one }) => ({ - user: one(users, { + user: one(usersTable, { fields: [collections.user_id], - references: [users.id], + references: [usersTable.id], }), })); diff --git a/src/db/schema/index.ts b/src/db/schema/index.ts index d755c0d..e7bf9ff 100644 --- a/src/db/schema/index.ts +++ b/src/db/schema/index.ts @@ -1,11 +1,11 @@ -export { default as users, user_relations, type Users } from './users'; +export { default as usersTable, userRelations as user_relations, type Users } from './users.table'; export { default as recoveryCodes, type RecoveryCodes } from './recoveryCodes'; export { default as password_reset_tokens, password_reset_token_relations, type PasswordResetTokens, } from './passwordResetTokens'; -export { default as sessions, type Sessions } from './sessions'; +export { default as sessionsTable, type Sessions } from './sessions.table'; export { default as roles, role_relations, type Roles } from './roles'; export { default as userRoles, user_role_relations, type UserRoles } from './userRoles'; export { default as collections, collection_relations, type Collections } from './collections'; diff --git a/src/db/schema/passwordResetTokens.ts b/src/db/schema/passwordResetTokens.ts index 7d0a18d..a849bc1 100644 --- a/src/db/schema/passwordResetTokens.ts +++ b/src/db/schema/passwordResetTokens.ts @@ -1,7 +1,7 @@ import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; import { createId as cuid2 } from '@paralleldrive/cuid2'; import { type InferSelectModel, relations } from 'drizzle-orm'; -import users from './users'; +import usersTable from './users.table'; import { timestamps } from '../utils'; const password_reset_tokens = pgTable('password_reset_tokens', { @@ -10,7 +10,7 @@ const password_reset_tokens = pgTable('password_reset_tokens', { .$defaultFn(() => cuid2()), user_id: uuid('user_id') .notNull() - .references(() => users.id, { onDelete: 'cascade' }), + .references(() => usersTable.id, { onDelete: 'cascade' }), expires_at: timestamp('expires_at'), ...timestamps, }); @@ -18,9 +18,9 @@ const password_reset_tokens = pgTable('password_reset_tokens', { export type PasswordResetTokens = InferSelectModel; export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({ - user: one(users, { + user: one(usersTable, { fields: [password_reset_tokens.user_id], - references: [users.id], + references: [usersTable.id], }), })); diff --git a/src/db/schema/recoveryCodes.ts b/src/db/schema/recoveryCodes.ts index c5433c0..b63c167 100644 --- a/src/db/schema/recoveryCodes.ts +++ b/src/db/schema/recoveryCodes.ts @@ -1,13 +1,13 @@ import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'; import type { InferSelectModel } from 'drizzle-orm'; -import users from './users'; +import usersTable from './users.table'; import { timestamps } from '../utils'; const recovery_codes = pgTable('recovery_codes', { id: uuid('id').primaryKey().defaultRandom(), userId: uuid('user_id') .notNull() - .references(() => users.id), + .references(() => usersTable.id), code: text('code').notNull(), used: boolean('used').default(false), ...timestamps, diff --git a/src/db/schema/sessions.ts b/src/db/schema/sessions.table.ts similarity index 50% rename from src/db/schema/sessions.ts rename to src/db/schema/sessions.table.ts index 79c3b21..813b2b8 100644 --- a/src/db/schema/sessions.ts +++ b/src/db/schema/sessions.table.ts @@ -1,12 +1,12 @@ import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; -import { type InferSelectModel } from 'drizzle-orm'; -import users from './users'; +import { relations, type InferSelectModel } from 'drizzle-orm'; +import usersTable from './users.table'; -const sessions = pgTable('sessions', { +const sessionsTable = pgTable('sessions', { id: text('id').primaryKey(), userId: uuid('user_id') .notNull() - .references(() => users.id), + .references(() => usersTable.id), expiresAt: timestamp('expires_at', { withTimezone: true, mode: 'date', @@ -17,6 +17,13 @@ const sessions = pgTable('sessions', { isTwoFactorAuthenticated: boolean('is_two_factor_authenticated').default(false), }); -export type Sessions = InferSelectModel; +export const sessionsRelations = relations(sessionsTable, ({ one }) => ({ + user: one(usersTable, { + fields: [sessionsTable.userId], + references: [usersTable.id], + }) +})); -export default sessions; +export type Sessions = InferSelectModel; + +export default sessionsTable; diff --git a/src/db/schema/two-factor.table.ts b/src/db/schema/two-factor.table.ts index a01262f..d488c1d 100644 --- a/src/db/schema/two-factor.table.ts +++ b/src/db/schema/two-factor.table.ts @@ -2,7 +2,7 @@ import { createId as cuid2 } from '@paralleldrive/cuid2'; import { type InferSelectModel, relations } from 'drizzle-orm'; import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; import { timestamps } from '../utils'; -import users from './users'; +import usersTable from './users.table'; const twoFactorTable = pgTable('two_factor', { id: uuid('id').primaryKey().defaultRandom(), @@ -17,15 +17,15 @@ const twoFactorTable = pgTable('two_factor', { }), userId: uuid('user_id') .notNull() - .references(() => users.id) + .references(() => usersTable.id) .unique(), ...timestamps, }); export const emailVerificationsRelations = relations(twoFactorTable, ({ one }) => ({ - user: one(users, { + user: one(usersTable, { fields: [twoFactorTable.userId], - references: [users.id], + references: [usersTable.id], }), })); diff --git a/src/db/schema/userRoles.ts b/src/db/schema/userRoles.ts index 3a87ca9..d241788 100644 --- a/src/db/schema/userRoles.ts +++ b/src/db/schema/userRoles.ts @@ -1,7 +1,7 @@ import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'; import { createId as cuid2 } from '@paralleldrive/cuid2'; import { type InferSelectModel, relations } from 'drizzle-orm'; -import users from './users'; +import usersTable from './users.table'; import roles from './roles'; import { timestamps } from '../utils'; @@ -12,7 +12,7 @@ const user_roles = pgTable('user_roles', { .$defaultFn(() => cuid2()), user_id: uuid('user_id') .notNull() - .references(() => users.id, { onDelete: 'cascade' }), + .references(() => usersTable.id, { onDelete: 'cascade' }), role_id: uuid('role_id') .notNull() .references(() => roles.id, { onDelete: 'cascade' }), @@ -25,9 +25,9 @@ export const user_role_relations = relations(user_roles, ({ one }) => ({ fields: [user_roles.role_id], references: [roles.id], }), - user: one(users, { + user: one(usersTable, { fields: [user_roles.user_id], - references: [users.id], + references: [usersTable.id], }), })); diff --git a/src/db/schema/users.ts b/src/db/schema/users.table.ts similarity index 79% rename from src/db/schema/users.ts rename to src/db/schema/users.table.ts index 3fb555b..e3e40cb 100644 --- a/src/db/schema/users.ts +++ b/src/db/schema/users.table.ts @@ -4,7 +4,7 @@ import { type InferSelectModel, relations } from 'drizzle-orm'; import { timestamps } from '../utils'; import user_roles from './userRoles'; -const users = pgTable('users', { +const usersTable = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), cuid: text('cuid') .unique() @@ -20,10 +20,10 @@ const users = pgTable('users', { ...timestamps, }); -export const user_relations = relations(users, ({ many }) => ({ +export const userRelations = relations(usersTable, ({ many }) => ({ user_roles: many(user_roles), })); -export type Users = InferSelectModel; +export type Users = InferSelectModel; -export default users; +export default usersTable; diff --git a/src/db/schema/wishlists.ts b/src/db/schema/wishlists.ts index db0c327..673308a 100644 --- a/src/db/schema/wishlists.ts +++ b/src/db/schema/wishlists.ts @@ -1,7 +1,7 @@ import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; import { createId as cuid2 } from '@paralleldrive/cuid2'; import { type InferSelectModel, relations } from 'drizzle-orm'; -import users from './users'; +import usersTable from './users.table'; import { timestamps } from '../utils'; const wishlists = pgTable('wishlists', { @@ -11,7 +11,7 @@ const wishlists = pgTable('wishlists', { .$defaultFn(() => cuid2()), user_id: uuid('user_id') .notNull() - .references(() => users.id, { onDelete: 'cascade' }), + .references(() => usersTable.id, { onDelete: 'cascade' }), name: text('name').notNull().default('My Wishlist'), ...timestamps, }); @@ -19,9 +19,9 @@ const wishlists = pgTable('wishlists', { export type Wishlists = InferSelectModel; export const wishlists_relations = relations(wishlists, ({ one }) => ({ - user: one(users, { + user: one(usersTable, { fields: [wishlists.user_id], - references: [users.id], + references: [usersTable.id], }), })); diff --git a/src/hooks.server.ts b/src/hooks.server.ts index f07cc18..f2b081b 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,8 +1,12 @@ // import * as Sentry from '@sentry/sveltekit'; +import { hc } from 'hono/client'; import { sequence } from '@sveltejs/kit/hooks'; -import type { Handle } from '@sveltejs/kit'; +import { redirect, type Handle } from '@sveltejs/kit'; import { dev } from '$app/environment'; import { lucia } from '$lib/server/auth'; +import type { ApiRoutes } from '$lib/server/api'; +import { parseApiResponse } from '$lib/utils/api'; +import { StatusCodes } from '$lib/constants/status-codes'; // TODO: Fix Sentry as it is not working on SvelteKit v2 // Sentry.init({ @@ -12,6 +16,39 @@ import { lucia } from '$lib/server/auth'; // enabled: !dev // }); +const apiClient: Handle = async ({ event, resolve }) => { + /* ------------------------------ Register api ------------------------------ */ + const { api } = hc('/', { + fetch: event.fetch, + headers: { + 'x-forwarded-for': event.getClientAddress(), + host: event.request.headers.get('host') || '' + } + }); + + /* ----------------------------- Auth functions ----------------------------- */ + async function getAuthedUser() { + const { data } = await api.iam.user.$get().then(parseApiResponse) + return data && data.user; + } + + async function getAuthedUserOrThrow() { + const { data } = await api.iam.user.$get().then(parseApiResponse); + if (!data || !data.user) throw redirect(StatusCodes.TEMPORARY_REDIRECT, '/'); + return data?.user; + } + + /* ------------------------------ Set contexts ------------------------------ */ + event.locals.api = api; + event.locals.parseApiResponse = parseApiResponse; + event.locals.getAuthedUser = getAuthedUser; + event.locals.getAuthedUserOrThrow = getAuthedUserOrThrow; + + /* ----------------------------- Return response ---------------------------- */ + const response = await resolve(event); + return response; +}; + export const authentication: Handle = async function ({ event, resolve }) { event.locals.startTimer = Date.now(); @@ -55,5 +92,6 @@ export const authentication: Handle = async function ({ event, resolve }) { export const handle: Handle = sequence( // Sentry.sentryHandle(), authentication, + apiClient ); // export const handleError = Sentry.handleErrorWithSentry(); diff --git a/src/lib/server/api/common/config.ts b/src/lib/server/api/common/config.ts new file mode 100644 index 0000000..c0a9225 --- /dev/null +++ b/src/lib/server/api/common/config.ts @@ -0,0 +1,14 @@ +import * as envs from '$env/static/private'; + +const isPreview = process.env.VERCEL_ENV === 'preview' || process.env.VERCEL_ENV === 'development'; + +let domain; +if (process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production') { + domain = 'boredgame.vercel.app'; +} else if (isPreview) { + domain = process.env.VERCEL_BRANCH_URL; +} else { + domain = 'localhost'; +} + +export const config = { ...envs, isProduction: process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production', domain }; diff --git a/src/lib/server/api/common/errors.ts b/src/lib/server/api/common/errors.ts new file mode 100644 index 0000000..fee54c4 --- /dev/null +++ b/src/lib/server/api/common/errors.ts @@ -0,0 +1,26 @@ +import { StatusCodes } from '$lib/constants/status-codes'; +import { HTTPException } from 'hono/http-exception'; + +export function TooManyRequests(message: string = 'Too many requests') { + return new HTTPException(StatusCodes.TOO_MANY_REQUESTS, { message }); +} + +export function Forbidden(message: string = 'Forbidden') { + return new HTTPException(StatusCodes.FORBIDDEN, { message }); +} + +export function Unauthorized(message: string = 'Unauthorized') { + return new HTTPException(StatusCodes.UNAUTHORIZED, { message }); +} + +export function NotFound(message: string = 'Not Found') { + return new HTTPException(StatusCodes.NOT_FOUND, { message }); +} + +export function BadRequest(message: string = 'Bad Request') { + return new HTTPException(StatusCodes.BAD_REQUEST, { message }); +} + +export function InternalError(message: string = 'Internal Error') { + return new HTTPException(StatusCodes.INTERNAL_SERVER_ERROR, { message }); +} diff --git a/src/lib/server/api/controllers/iam.controller.ts b/src/lib/server/api/controllers/iam.controller.ts new file mode 100644 index 0000000..f52fe78 --- /dev/null +++ b/src/lib/server/api/controllers/iam.controller.ts @@ -0,0 +1,10 @@ +import { Hono } from 'hono'; +import { requireAuth } from "../middleware/auth.middleware"; + +const users = new Hono().get('/me', requireAuth, async (c) => { + const user = c.var.user; + return c.json({ user }); +}); + +export default users; +export type UsersType = typeof users \ No newline at end of file diff --git a/src/lib/server/api/index.ts b/src/lib/server/api/index.ts new file mode 100644 index 0000000..960cdd4 --- /dev/null +++ b/src/lib/server/api/index.ts @@ -0,0 +1,23 @@ +import { Hono } from 'hono'; +import { hc } from 'hono/client'; +import { validateAuthSession, verifyOrigin } from './middleware/auth.middleware'; +import users from './controllers/iam.controller'; +import { config } from './common/config'; + +/* ----------------------------------- Api ---------------------------------- */ +const app = new Hono().basePath('/api'); + +/* --------------------------- Global Middlewares --------------------------- */ +app.use(verifyOrigin).use(validateAuthSession); + +/* --------------------------------- Routes --------------------------------- */ +const routes = app + .route('/iam', users) + +/* -------------------------------------------------------------------------- */ +/* Exports */ +/* -------------------------------------------------------------------------- */ +export const rpc = hc(config.ORIGIN); +export type ApiClient = typeof rpc; +export type ApiRoutes = typeof routes; +export { app }; \ No newline at end of file diff --git a/src/lib/server/api/infrastructure/auth/lucia.ts b/src/lib/server/api/infrastructure/auth/lucia.ts new file mode 100644 index 0000000..a9873c1 --- /dev/null +++ b/src/lib/server/api/infrastructure/auth/lucia.ts @@ -0,0 +1,56 @@ +// lib/server/lucia.ts +import { Lucia, TimeSpan } from 'lucia'; +import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle'; +import db from '$db/index'; +import { sessionsTable, usersTable } from '$db/schema'; +import { config } from '../../common/config'; + +const adapter = new DrizzlePostgreSQLAdapter(db, sessionsTable, usersTable); + +export const lucia = new Lucia(adapter, { + getSessionAttributes: (attributes) => { + return { + ipCountry: attributes.ip_country, + ipAddress: attributes.ip_address, + isTwoFactorAuthEnabled: attributes.twoFactorAuthEnabled, + isTwoFactorAuthenticated: attributes.isTwoFactorAuthenticated, + }; + }, + getUserAttributes: (attributes) => { + return { + ...attributes, + }; + }, + sessionExpiresIn: new TimeSpan(30, 'd'), // 30 days + sessionCookie: { + name: 'session', + expires: false, // session cookies have very long lifespan (2 years) + attributes: { + // set to `true` when using HTTPS + secure: config.isProduction, + sameSite: 'strict', + domain: config.domain, + }, + }, +}); + +declare module 'lucia' { + interface Register { + Lucia: typeof lucia; + DatabaseUserAttributes: DatabaseUserAttributes; + DatabaseSessionAttributes: DatabaseSessionAttributes; + } + interface DatabaseSessionAttributes { + ip_country: string; + ip_address: string; + twoFactorAuthEnabled: boolean; + isTwoFactorAuthenticated: boolean; + } + interface DatabaseUserAttributes { + username: string; + email: string; + firstName: string; + lastName: string; + theme: string; + } +} diff --git a/src/lib/server/api/middleware/auth.middleware.ts b/src/lib/server/api/middleware/auth.middleware.ts new file mode 100644 index 0000000..9c21ea8 --- /dev/null +++ b/src/lib/server/api/middleware/auth.middleware.ts @@ -0,0 +1,50 @@ +import type { MiddlewareHandler } from 'hono'; +import { createMiddleware } from 'hono/factory'; +import type { HonoTypes } from '../types'; +import { lucia } from '../infrastructure/auth/lucia'; +import { verifyRequestOrigin } from 'lucia'; +import type { Session, User } from 'lucia'; +import { Unauthorized } from '../common/errors'; + +export const verifyOrigin: MiddlewareHandler = createMiddleware(async (c, next) => { + if (c.req.method === "GET") { + return next(); + } + const originHeader = c.req.header("Origin") ?? null; + const hostHeader = c.req.header("Host") ?? null; + if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) { + return c.body(null, 403); + } + return next(); +}) + +export const validateAuthSession: MiddlewareHandler = createMiddleware(async (c, next) => { + const sessionId = lucia.readSessionCookie(c.req.header("Cookie") ?? ""); + if (!sessionId) { + c.set("user", null); + c.set("session", null); + return next(); + } + + const { session, user } = await lucia.validateSession(sessionId); + if (session && session.fresh) { + c.header("Set-Cookie", lucia.createSessionCookie(session.id).serialize(), { append: true }); + } + if (!session) { + c.header("Set-Cookie", lucia.createBlankSessionCookie().serialize(), { append: true }); + } + c.set("session", session); + c.set("user", user); + return next(); +}) + +export const requireAuth: MiddlewareHandler<{ + Variables: { + session: Session; + user: User; + }; +}> = createMiddleware(async (c, next) => { + const user = c.var.user; + if (!user) throw Unauthorized('You must be logged in to access this resource'); + return next(); +}); diff --git a/src/lib/server/api/middleware/rate-limiter.middleware.ts b/src/lib/server/api/middleware/rate-limiter.middleware.ts new file mode 100644 index 0000000..d704571 --- /dev/null +++ b/src/lib/server/api/middleware/rate-limiter.middleware.ts @@ -0,0 +1,32 @@ +import { rateLimiter } from "hono-rate-limiter"; +import { RedisStore } from 'rate-limit-redis' +import RedisClient from 'ioredis' +import type { HonoTypes } from "../types"; +import { config } from "../common/config"; + +const client = new RedisClient(config.REDIS_URL) + +export function limiter({ limit, minutes, key = "" }: { + limit: number; + minutes: number; + key?: string; +}) { + return rateLimiter({ + windowMs: minutes * 60 * 1000, // every x minutes + limit, // Limit each IP to 100 requests per `window` (here, per 15 minutes). + standardHeaders: "draft-6", // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header + keyGenerator: (c) => { + const vars = c.var as HonoTypes['Variables']; + const clientKey = vars.user?.id || c.req.header("x-forwarded-for"); + const pathKey = key || c.req.routePath; + return `${clientKey}_${pathKey}` + }, // Method to generate custom identifiers for clients. + // Redis store configuration + store: new RedisStore({ + // @ts-expect-error - Known issue: the `call` function is not present in @types/ioredis + sendCommand: (...args: string[]) => client.call(...args), + }) as any, + }) +} + + diff --git a/src/lib/server/api/types/index.ts b/src/lib/server/api/types/index.ts new file mode 100644 index 0000000..b0fcae1 --- /dev/null +++ b/src/lib/server/api/types/index.ts @@ -0,0 +1,14 @@ +import type { Promisify, RateLimitInfo } from 'hono-rate-limiter'; +import type { Session, User } from 'lucia'; + +export type HonoTypes = { + Variables: { + session: Session | null; + user: User | null; + rateLimit: RateLimitInfo; + rateLimitStore: { + getKey?: (key: string) => Promisify; + resetKey: (key: string) => Promisify; + }; + }; +}; diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index d312b89..f6b8b69 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -2,9 +2,9 @@ import { Lucia, TimeSpan } from 'lucia'; import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle'; import db from '../../db'; -import { sessions, users } from '$db/schema'; +import { sessionsTable, usersTable } from '$db/schema'; -const adapter = new DrizzlePostgreSQLAdapter(db, sessions, users); +const adapter = new DrizzlePostgreSQLAdapter(db, sessionsTable, usersTable); let domain; if (process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production') { diff --git a/src/lib/utils/api.ts b/src/lib/utils/api.ts new file mode 100644 index 0000000..79c142b --- /dev/null +++ b/src/lib/utils/api.ts @@ -0,0 +1,25 @@ +import type { ClientResponse } from "hono/client"; + +export async function parseApiResponse(response: ClientResponse) { + if (response.status === 204 || response.headers.get('Content-Length') === '0') { + return response.ok + ? { data: null, error: null, response } + : { data: null, error: 'An unknown error has occured', response }; + } + + if (response.ok) { + const data = await response.json() as T; + + return { data, error: null, status: response.status }; + } + + // handle errors + let error = await response.text(); + try { + error = JSON.parse(error); // attempt to parse as JSON + } catch { + // noop + return { data: null, error, response }; + } + return { data: null, error, response }; +} diff --git a/src/routes/api/[...slug]/+server.ts b/src/routes/api/[...slug]/+server.ts new file mode 100644 index 0000000..5b4a8e3 --- /dev/null +++ b/src/routes/api/[...slug]/+server.ts @@ -0,0 +1,9 @@ +import { app } from '$lib/server/api'; +import type { RequestHandler } from '@sveltejs/kit'; + +export const GET: RequestHandler = ({ request }) => app.request(request); +export const PUT: RequestHandler = ({ request }) => app.request(request); +export const DELETE: RequestHandler = ({ request }) => app.fetch(request); +export const POST: RequestHandler = ({ request }) => app.fetch(request); +export const PATCH: RequestHandler = ({ request }) => app.fetch(request); +export const fallback: RequestHandler = ({ request }) => app.fetch(request);