diff --git a/.env b/.env new file mode 100644 index 0000000..976b6b1 --- /dev/null +++ b/.env @@ -0,0 +1,14 @@ +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/vash" + +ORIGIN="http://localhost:5173" + +# Better Auth +# For production use 32 characters and generated with high entropy +# https://www.better-auth.com/docs/installation +BETTER_AUTH_SECRET="***REMOVED***" + +OSU_CLIENT_ID="30152" +OSU_CLIENT_SECRET="***REMOVED***" + +OSU_IRC_USERNAME="jollyWudchip" +OSU_IRC_PASSWORD="***REMOVED***" diff --git a/.svelte-kit/adapter-node/.vite/manifest.json b/.svelte-kit/adapter-node/.vite/manifest.json new file mode 100644 index 0000000..36de7fb --- /dev/null +++ b/.svelte-kit/adapter-node/.vite/manifest.json @@ -0,0 +1,661 @@ +{ + ".svelte-kit/generated/server/internal.js": { + "file": "internal.js", + "name": "internal", + "src": ".svelte-kit/generated/server/internal.js", + "isEntry": true, + "imports": [ + "_root.js", + "_environment.js", + "_server.js", + "_internal.js", + "_shared-server.js" + ] + }, + "_api.js": { + "file": "chunks/api.js", + "name": "api", + "imports": [ + "_shared-server.js", + "_index2.js" + ] + }, + "_auth.js": { + "file": "chunks/auth.js", + "name": "auth", + "imports": [ + "_shared-server.js", + "_url.js", + "_root.js", + "_utils.js", + "_query.js", + "_index2.js" + ], + "dynamicImports": [ + "node_modules/better-auth/dist/adapters/memory-adapter/index.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/bun-sqlite-dialect.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/node-sqlite-dialect.mjs" + ] + }, + "_engine.js": { + "file": "chunks/engine.js", + "name": "engine", + "imports": [ + "_index2.js" + ] + }, + "_environment.js": { + "file": "chunks/environment.js", + "name": "environment" + }, + "_exports.js": { + "file": "chunks/exports.js", + "name": "exports" + }, + "_false.js": { + "file": "chunks/false.js", + "name": "false" + }, + "_index.js": { + "file": "chunks/index.js", + "name": "index", + "imports": [ + "_index3.js", + "_root.js" + ] + }, + "_index2.js": { + "file": "chunks/index2.js", + "name": "index", + "imports": [ + "_shared-server.js" + ] + }, + "_index3.js": { + "file": "chunks/index3.js", + "name": "index" + }, + "_index4.js": { + "file": "chunks/index4.js", + "name": "index", + "imports": [ + "_state.svelte.js", + "_url.js", + "_utils.js", + "_index.js", + "_root.js", + "_exports.js", + "_index3.js" + ] + }, + "_internal.js": { + "file": "chunks/internal.js", + "name": "internal", + "imports": [ + "_root.js", + "_environment.js", + "_server.js", + "_shared-server.js" + ], + "dynamicImports": [ + "src/hooks.server.ts" + ] + }, + "_orchestrator.js": { + "file": "chunks/orchestrator.js", + "name": "orchestrator", + "imports": [ + "_index2.js", + "_engine.js", + "_shared-server.js" + ] + }, + "_query.js": { + "file": "chunks/query.js", + "name": "query", + "imports": [ + "_shared.js", + "_environment.js" + ] + }, + "_root.js": { + "file": "chunks/root.js", + "name": "root", + "imports": [ + "_index3.js", + "_false.js" + ] + }, + "_routing.js": { + "file": "chunks/routing.js", + "name": "routing", + "imports": [ + "_url.js" + ] + }, + "_server.js": { + "file": "chunks/server.js", + "name": "server" + }, + "_shared-server.js": { + "file": "chunks/shared-server.js", + "name": "shared-server" + }, + "_shared.js": { + "file": "chunks/shared.js", + "name": "shared", + "imports": [ + "_utils.js" + ] + }, + "_state.svelte.js": { + "file": "chunks/state.svelte.js", + "name": "state.svelte", + "imports": [ + "_index3.js", + "_url.js", + "_root.js" + ] + }, + "_url.js": { + "file": "chunks/url.js", + "name": "url" + }, + "_utils.js": { + "file": "chunks/utils.js", + "name": "utils" + }, + "node_modules/@sveltejs/kit/src/runtime/app/server/remote/index.js": { + "file": "remote-entry.js", + "name": "remote-entry", + "src": "node_modules/@sveltejs/kit/src/runtime/app/server/remote/index.js", + "isEntry": true, + "imports": [ + "_query.js", + "_shared.js", + "_false.js", + "_server.js" + ] + }, + "node_modules/@sveltejs/kit/src/runtime/components/svelte-5/layout.svelte": { + "file": "entries/fallbacks/layout.svelte.js", + "name": "entries/fallbacks/layout.svelte", + "src": "node_modules/@sveltejs/kit/src/runtime/components/svelte-5/layout.svelte", + "isEntry": true + }, + "node_modules/@sveltejs/kit/src/runtime/server/index.js": { + "file": "index.js", + "name": "index", + "src": "node_modules/@sveltejs/kit/src/runtime/server/index.js", + "isEntry": true, + "imports": [ + "_false.js", + "_server.js", + "_shared.js", + "_url.js", + "_utils.js", + "_index.js", + "_shared-server.js", + "_routing.js", + "_exports.js", + "_internal.js" + ] + }, + "node_modules/better-auth/dist/adapters/kysely-adapter/bun-sqlite-dialect.mjs": { + "file": "chunks/bun-sqlite-dialect.js", + "name": "bun-sqlite-dialect", + "src": "node_modules/better-auth/dist/adapters/kysely-adapter/bun-sqlite-dialect.mjs", + "isDynamicEntry": true + }, + "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs": { + "file": "chunks/index6.js", + "name": "index", + "src": "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs", + "isDynamicEntry": true, + "imports": [ + "_auth.js" + ] + }, + "node_modules/better-auth/dist/adapters/kysely-adapter/node-sqlite-dialect.mjs": { + "file": "chunks/node-sqlite-dialect.js", + "name": "node-sqlite-dialect", + "src": "node_modules/better-auth/dist/adapters/kysely-adapter/node-sqlite-dialect.mjs", + "isDynamicEntry": true + }, + "node_modules/better-auth/dist/adapters/memory-adapter/index.mjs": { + "file": "chunks/index5.js", + "name": "index", + "src": "node_modules/better-auth/dist/adapters/memory-adapter/index.mjs", + "isDynamicEntry": true + }, + "src/hooks.server.ts": { + "file": "entries/hooks.server.js", + "name": "entries/hooks.server", + "src": "src/hooks.server.ts", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_environment.js", + "_auth.js", + "_index2.js" + ] + }, + "src/routes/(app)/+layout.server.ts": { + "file": "entries/pages/(app)/_layout.server.ts.js", + "name": "entries/pages/(app)/_layout.server.ts", + "src": "src/routes/(app)/+layout.server.ts", + "isEntry": true + }, + "src/routes/(app)/+layout.svelte": { + "file": "entries/pages/(app)/_layout.svelte.js", + "name": "entries/pages/(app)/_layout.svelte", + "src": "src/routes/(app)/+layout.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_index4.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ], + "css": [ + "_app/immutable/assets/_layout.Dv2no-8m.css" + ] + }, + "src/routes/(app)/+page.server.ts": { + "file": "entries/pages/(app)/_page.server.ts.js", + "name": "entries/pages/(app)/_page.server.ts", + "src": "src/routes/(app)/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/+page.svelte": { + "file": "entries/pages/(app)/_page.svelte.js", + "name": "entries/pages/(app)/_page.svelte", + "src": "src/routes/(app)/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js" + ] + }, + "src/routes/(app)/admin/+page.server.ts": { + "file": "entries/pages/(app)/admin/_page.server.ts.js", + "name": "entries/pages/(app)/admin/_page.server.ts", + "src": "src/routes/(app)/admin/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/admin/+page.svelte": { + "file": "entries/pages/(app)/admin/_page.svelte.js", + "name": "entries/pages/(app)/admin/_page.svelte", + "src": "src/routes/(app)/admin/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/mappools/+page.server.ts": { + "file": "entries/pages/(app)/mappools/_page.server.ts.js", + "name": "entries/pages/(app)/mappools/_page.server.ts", + "src": "src/routes/(app)/mappools/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/mappools/+page.svelte": { + "file": "entries/pages/(app)/mappools/_page.svelte.js", + "name": "entries/pages/(app)/mappools/_page.svelte", + "src": "src/routes/(app)/mappools/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/mappools/[id]/+page.server.ts": { + "file": "entries/pages/(app)/mappools/_id_/_page.server.ts.js", + "name": "entries/pages/(app)/mappools/_id_/_page.server.ts", + "src": "src/routes/(app)/mappools/[id]/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_api.js" + ] + }, + "src/routes/(app)/mappools/[id]/+page.svelte": { + "file": "entries/pages/(app)/mappools/_id_/_page.svelte.js", + "name": "entries/pages/(app)/mappools/_id_/_page.svelte", + "src": "src/routes/(app)/mappools/[id]/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/matches/+page.server.ts": { + "file": "entries/pages/(app)/matches/_page.server.ts.js", + "name": "entries/pages/(app)/matches/_page.server.ts", + "src": "src/routes/(app)/matches/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_engine.js", + "_orchestrator.js" + ] + }, + "src/routes/(app)/matches/+page.svelte": { + "file": "entries/pages/(app)/matches/_page.svelte.js", + "name": "entries/pages/(app)/matches/_page.svelte", + "src": "src/routes/(app)/matches/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/matches/[id]/+page.server.ts": { + "file": "entries/pages/(app)/matches/_id_/_page.server.ts.js", + "name": "entries/pages/(app)/matches/_id_/_page.server.ts", + "src": "src/routes/(app)/matches/[id]/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_engine.js", + "_orchestrator.js", + "_api.js" + ] + }, + "src/routes/(app)/matches/[id]/+page.svelte": { + "file": "entries/pages/(app)/matches/_id_/_page.svelte.js", + "name": "entries/pages/(app)/matches/_id_/_page.svelte", + "src": "src/routes/(app)/matches/[id]/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/settings/+page.server.ts": { + "file": "entries/pages/(app)/settings/_page.server.ts.js", + "name": "entries/pages/(app)/settings/_page.server.ts", + "src": "src/routes/(app)/settings/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js", + "_index2.js" + ] + }, + "src/routes/(app)/settings/+page.svelte": { + "file": "entries/pages/(app)/settings/_page.svelte.js", + "name": "entries/pages/(app)/settings/_page.svelte", + "src": "src/routes/(app)/settings/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/teams/+page.server.ts": { + "file": "entries/pages/(app)/teams/_page.server.ts.js", + "name": "entries/pages/(app)/teams/_page.server.ts", + "src": "src/routes/(app)/teams/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/teams/+page.svelte": { + "file": "entries/pages/(app)/teams/_page.svelte.js", + "name": "entries/pages/(app)/teams/_page.svelte", + "src": "src/routes/(app)/teams/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/+error.svelte": { + "file": "entries/pages/_error.svelte.js", + "name": "entries/pages/_error.svelte", + "src": "src/routes/+error.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_index4.js" + ] + }, + "src/routes/api/health/+server.ts": { + "file": "entries/endpoints/api/health/_server.ts.js", + "name": "entries/endpoints/api/health/_server.ts", + "src": "src/routes/api/health/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/+server.ts": { + "file": "entries/endpoints/api/mappools/_server.ts.js", + "name": "entries/endpoints/api/mappools/_server.ts", + "src": "src/routes/api/mappools/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/[id]/+server.ts": { + "file": "entries/endpoints/api/mappools/_id_/_server.ts.js", + "name": "entries/endpoints/api/mappools/_id_/_server.ts", + "src": "src/routes/api/mappools/[id]/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/[id]/slots/+server.ts": { + "file": "entries/endpoints/api/mappools/_id_/slots/_server.ts.js", + "name": "entries/endpoints/api/mappools/_id_/slots/_server.ts", + "src": "src/routes/api/mappools/[id]/slots/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/[id]/slots/[slotId]/+server.ts": { + "file": "entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js", + "name": "entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts", + "src": "src/routes/api/mappools/[id]/slots/[slotId]/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/matches/+server.ts": { + "file": "entries/endpoints/api/matches/_server.ts.js", + "name": "entries/endpoints/api/matches/_server.ts", + "src": "src/routes/api/matches/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/_server.ts", + "src": "src/routes/api/matches/[id]/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/lobby/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/lobby/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/lobby/_server.ts", + "src": "src/routes/api/matches/[id]/lobby/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/pick/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/pick/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/pick/_server.ts", + "src": "src/routes/api/matches/[id]/pick/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/roll/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/roll/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/roll/_server.ts", + "src": "src/routes/api/matches/[id]/roll/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/rolling/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/rolling/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/rolling/_server.ts", + "src": "src/routes/api/matches/[id]/rolling/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/score/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/score/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/score/_server.ts", + "src": "src/routes/api/matches/[id]/score/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/osu/beatmap/[id]/+server.ts": { + "file": "entries/endpoints/api/osu/beatmap/_id_/_server.ts.js", + "name": "entries/endpoints/api/osu/beatmap/_id_/_server.ts", + "src": "src/routes/api/osu/beatmap/[id]/+server.ts", + "isEntry": true, + "imports": [ + "_api.js" + ] + }, + "src/routes/api/queue/+server.ts": { + "file": "entries/endpoints/api/queue/_server.ts.js", + "name": "entries/endpoints/api/queue/_server.ts", + "src": "src/routes/api/queue/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js", + "_index2.js" + ] + }, + "src/routes/demo/+page.svelte": { + "file": "entries/pages/demo/_page.svelte.js", + "name": "entries/pages/demo/_page.svelte", + "src": "src/routes/demo/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_server.js", + "_routing.js", + "_url.js", + "_root.js" + ] + }, + "src/routes/demo/better-auth/+page.server.ts": { + "file": "entries/pages/demo/better-auth/_page.server.ts.js", + "name": "entries/pages/demo/better-auth/_page.server.ts", + "src": "src/routes/demo/better-auth/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js" + ] + }, + "src/routes/demo/better-auth/+page.svelte": { + "file": "entries/pages/demo/better-auth/_page.svelte.js", + "name": "entries/pages/demo/better-auth/_page.svelte", + "src": "src/routes/demo/better-auth/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/demo/better-auth/login/+page.server.ts": { + "file": "entries/pages/demo/better-auth/login/_page.server.ts.js", + "name": "entries/pages/demo/better-auth/login/_page.server.ts", + "src": "src/routes/demo/better-auth/login/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js" + ] + }, + "src/routes/demo/better-auth/login/+page.svelte": { + "file": "entries/pages/demo/better-auth/login/_page.svelte.js", + "name": "entries/pages/demo/better-auth/login/_page.svelte", + "src": "src/routes/demo/better-auth/login/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/login/+page.server.ts": { + "file": "entries/pages/login/_page.server.ts.js", + "name": "entries/pages/login/_page.server.ts", + "src": "src/routes/login/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js" + ] + } +} \ No newline at end of file diff --git a/.svelte-kit/adapter-node/_app/immutable/assets/_layout.Dv2no-8m.css b/.svelte-kit/adapter-node/_app/immutable/assets/_layout.Dv2no-8m.css new file mode 100644 index 0000000..d32e9ae --- /dev/null +++ b/.svelte-kit/adapter-node/_app/immutable/assets/_layout.Dv2no-8m.css @@ -0,0 +1 @@ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:"Geist Mono", ui-monospace, monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-500:oklch(65.6% .241 354.308);--color-gray-300:oklch(87.2% .01 258.338);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-6xl:3.75rem;--text-6xl--line-height:1;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--blur-md:12px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface-900:#0a0a0f;--color-surface-800:#12121a;--color-surface-700:#1a1a25;--color-surface-600:#24243a;--color-accent:#ff66ab;--color-accent-hover:#ff85be;--color-accent-dim:#ff66ab33;--color-text-primary:#eeeef0;--color-text-secondary:#8888a0;--color-border:#2a2a3d;--font-body:"Geist", system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.mx-auto{margin-inline:auto}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-56{margin-left:calc(var(--spacing) * 56)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.table{display:table}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-full{height:100%}.max-h-\[80vh\]{max-height:80vh}.min-h-\[70vh\]{min-height:70vh}.min-h-dvh{min-height:100dvh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.rotate-90{rotate:90deg}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[auto_1fr_auto_auto_auto\]{grid-template-columns:auto 1fr auto auto auto}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent\/20{border-color:#ff66ab33}@supports (color:color-mix(in lab,red,red)){.border-accent\/20{border-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.border-accent\/30{border-color:#ff66ab4d}@supports (color:color-mix(in lab,red,red)){.border-accent\/30{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/30{border-color:color-mix(in oklab,var(--color-blue-500) 30%,transparent)}}.border-border{border-color:var(--color-border)}.border-border\/50{border-color:#2a2a3d80}@supports (color:color-mix(in lab,red,red)){.border-border\/50{border-color:color-mix(in oklab,var(--color-border) 50%,transparent)}}.border-gray-300{border-color:var(--color-gray-300)}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.border-green-500\/30{border-color:#00c7584d}@supports (color:color-mix(in lab,red,red)){.border-green-500\/30{border-color:color-mix(in oklab,var(--color-green-500) 30%,transparent)}}.border-green-500\/40{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.border-green-500\/40{border-color:color-mix(in oklab,var(--color-green-500) 40%,transparent)}}.border-pink-500\/30{border-color:#f6339a4d}@supports (color:color-mix(in lab,red,red)){.border-pink-500\/30{border-color:color-mix(in oklab,var(--color-pink-500) 30%,transparent)}}.border-purple-500\/30{border-color:#ac4bff4d}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/30{border-color:color-mix(in oklab,var(--color-purple-500) 30%,transparent)}}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500) 30%,transparent)}}.border-yellow-500\/30{border-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/30{border-color:color-mix(in oklab,var(--color-yellow-500) 30%,transparent)}}.bg-accent{background-color:var(--color-accent)}.bg-accent-dim{background-color:var(--color-accent-dim)}.bg-accent\/10{background-color:#ff66ab1a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500) 10%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.bg-blue-600{background-color:var(--color-blue-600)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500\/5{background-color:#00c7580d}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/5{background-color:color-mix(in oklab,var(--color-green-500) 5%,transparent)}}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/20{background-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.bg-pink-500\/20{background-color:#f6339a33}@supports (color:color-mix(in lab,red,red)){.bg-pink-500\/20{background-color:color-mix(in oklab,var(--color-pink-500) 20%,transparent)}}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500) 10%,transparent)}}.bg-purple-500\/20{background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/20{background-color:color-mix(in oklab,var(--color-purple-500) 20%,transparent)}}.bg-red-500\/5{background-color:#fb2c360d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/5{background-color:color-mix(in oklab,var(--color-red-500) 5%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.bg-surface-600{background-color:var(--color-surface-600)}.bg-surface-700{background-color:var(--color-surface-700)}.bg-surface-800{background-color:var(--color-surface-800)}.bg-surface-900{background-color:var(--color-surface-900)}.bg-surface-900\/80{background-color:#0a0a0fcc}@supports (color:color-mix(in lab,red,red)){.bg-surface-900\/80{background-color:color-mix(in oklab,var(--color-surface-900) 80%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-yellow-500\/5{background-color:#edb2000d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/5{background-color:color-mix(in oklab,var(--color-yellow-500) 5%,transparent)}}.bg-yellow-500\/20{background-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/20{background-color:color-mix(in oklab,var(--color-yellow-500) 20%,transparent)}}.object-cover{object-fit:cover}.p-1{padding:calc(var(--spacing) * 1)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-accent{color:var(--color-accent)}.text-blue-400{color:var(--color-blue-400)}.text-green-400{color:var(--color-green-400)}.text-pink-400{color:var(--color-pink-400)}.text-purple-400{color:var(--color-purple-400)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-surface-900{color:var(--color-surface-900)}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-text-secondary\/30{color:#8888a04d}@supports (color:color-mix(in lab,red,red)){.text-text-secondary\/30{color:color-mix(in oklab,var(--color-text-secondary) 30%,transparent)}}.text-white{color:var(--color-white)}.text-yellow-400{color:var(--color-yellow-400)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.group-hover\:text-accent:is(:where(.group):hover *){color:var(--color-accent)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-text-secondary\/50::placeholder{color:#8888a080}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-text-secondary\/50::placeholder{color:color-mix(in oklab,var(--color-text-secondary) 50%,transparent)}}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:border-accent:hover{border-color:var(--color-accent)}.hover\:border-accent\/30:hover{border-color:#ff66ab4d}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/30:hover{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.hover\:border-accent\/40:hover{border-color:#ff66ab66}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/40:hover{border-color:color-mix(in oklab,var(--color-accent) 40%,transparent)}}.hover\:border-green-500\/40:hover{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.hover\:border-green-500\/40:hover{border-color:color-mix(in oklab,var(--color-green-500) 40%,transparent)}}.hover\:border-red-500\/30:hover{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.hover\:border-red-500\/30:hover{border-color:color-mix(in oklab,var(--color-red-500) 30%,transparent)}}.hover\:bg-accent-hover:hover{background-color:var(--color-accent-hover)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.hover\:bg-surface-600:hover{background-color:var(--color-surface-600)}.hover\:bg-surface-700:hover{background-color:var(--color-surface-700)}.hover\:bg-yellow-500\/10:hover{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-500\/10:hover{background-color:color-mix(in oklab,var(--color-yellow-500) 10%,transparent)}}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:64rem){.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}html{background-color:var(--color-surface-900);color:var(--color-text-primary);font-family:var(--font-body)}body{min-height:100dvh}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:var(--color-surface-900)}::-webkit-scrollbar-thumb{background:var(--color-surface-600);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--color-accent)}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}} diff --git a/.svelte-kit/adapter-node/chunks/api.js b/.svelte-kit/adapter-node/chunks/api.js new file mode 100644 index 0000000..5e6cc8c --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/api.js @@ -0,0 +1,69 @@ +import { b as private_env } from "./shared-server.js"; +import "./index2.js"; +const CACHE_TTL = 10 * 60 * 1e3; +const MAX_ENTRIES = 500; +const cache = /* @__PURE__ */ new Map(); +function getCached(key) { + const entry = cache.get(key); + if (!entry) return null; + if (Date.now() > entry.expiresAt) { + cache.delete(key); + return null; + } + return entry.data; +} +function setCache(key, data, ttl = CACHE_TTL) { + if (cache.size >= MAX_ENTRIES) { + const firstKey = cache.keys().next().value; + if (firstKey) cache.delete(firstKey); + } + cache.set(key, { data, expiresAt: Date.now() + ttl }); +} +let clientToken = null; +let clientTokenExpiresAt = 0; +async function getClientToken() { + if (clientToken && Date.now() < clientTokenExpiresAt - 6e4) { + return clientToken; + } + const res = await fetch("https://osu.ppy.sh/oauth/token", { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + client_id: private_env.OSU_CLIENT_ID, + client_secret: private_env.OSU_CLIENT_SECRET, + grant_type: "client_credentials", + scope: "identify public" + }) + }); + if (!res.ok) { + throw new Error(`Failed to get osu! client token: ${res.status} ${res.statusText}`); + } + const data = await res.json(); + clientToken = data.access_token; + clientTokenExpiresAt = Date.now() + data.expires_in * 1e3; + return clientToken; +} +async function osuFetch(path, token) { + const t = await getClientToken(); + const res = await fetch(`https://osu.ppy.sh/api/v2${path}`, { + headers: { Authorization: `Bearer ${t}` } + }); + if (!res.ok) { + throw new Error(`osu! API ${path}: ${res.status} ${res.statusText}`); + } + return res.json(); +} +async function getBeatmap(beatmapId) { + const cacheKey = `beatmap:${beatmapId}`; + const cached = getCached(cacheKey); + if (cached) return cached; + const data = await osuFetch(`/beatmaps/${beatmapId}`); + setCache(cacheKey, data); + return data; +} +export { + getBeatmap as g +}; diff --git a/.svelte-kit/adapter-node/chunks/auth.js b/.svelte-kit/adapter-node/chunks/auth.js new file mode 100644 index 0000000..588593c --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/auth.js @@ -0,0 +1,7983 @@ +import { env, isTest, isDevelopment, logger, createLogger, isProduction, shouldPublishLog } from "@better-auth/core/env"; +import { BetterAuthError, BASE_ERROR_CODES } from "@better-auth/core/error"; +import { isValidIP, normalizeIP, deprecate, normalizePathname, createRateLimitKey, safeJSONParse, generateId, filterOutputFields, defineErrorCodes } from "@better-auth/core/utils"; +import { defineRequestState, getCurrentAuthContext, getCurrentAdapter, runWithTransaction, hasRequestState, runWithRequestState, runWithEndpointContext, runWithAdapter, getBetterAuthVersion } from "@better-auth/core/context"; +import { APIError, toResponse, createRouter } from "better-call"; +import { createAuthMiddleware, createAuthEndpoint } from "@better-auth/core/api"; +import * as import___better_auth_core_db from "@better-auth/core/db"; +import { getAuthTables } from "@better-auth/core/db"; +import * as z from "zod"; +import { Kysely, SqliteDialect, MysqlDialect, PostgresDialect, MssqlDialect, sql } from "kysely"; +import { initGetModelName, initGetFieldName, createAdapterFactory } from "@better-auth/core/db/adapter"; +import { hkdf } from "@noble/hashes/hkdf.js"; +import { sha256 } from "@noble/hashes/sha2.js"; +import { jwtDecrypt, calculateJwkThumbprint, base64url, jwtVerify, EncryptJWT, SignJWT, decodeJwt } from "jose"; +import { hex } from "@better-auth/utils/hex"; +import { createRandomStringGenerator } from "@better-auth/utils/random"; +import "@better-auth/utils"; +import { createHash } from "@better-auth/utils/hash"; +import { base64Url } from "@better-auth/utils/base64"; +import { binary } from "@better-auth/utils/binary"; +import { createHMAC } from "@better-auth/utils/hmac"; +import { xchacha20poly1305 } from "@noble/ciphers/chacha.js"; +import { hexToBytes as hexToBytes$1, managedNonce, utf8ToBytes, bytesToHex } from "@noble/ciphers/utils.js"; +import { SocialProviderListEnum, socialProviders } from "@better-auth/core/social-providers"; +import { JWTExpired } from "jose/errors"; +import defu$1, { createDefu, defu } from "defu"; +import { scryptAsync } from "@noble/hashes/scrypt.js"; +import { hexToBytes } from "@noble/hashes/utils.js"; +import { createTelemetry } from "@better-auth/telemetry"; +import { count, desc, asc, inArray, notInArray, like, lt, lte, ne, gt, gte, eq, and, or, sql as sql$1 } from "drizzle-orm"; +import { b as private_env } from "./shared-server.js"; +import "./url.js"; +import { getRequestEvent } from "@sveltejs/kit/internal/server"; +import "./root.js"; +import "./utils.js"; +import "@sveltejs/kit"; +import "@sveltejs/kit/internal"; +import "./query.js"; +import { d as db, c as team, t as teamMember } from "./index2.js"; +import { betterFetch } from "@better-fetch/fetch"; +import { createAuthorizationURL, validateAuthorizationCode, refreshAccessToken } from "@better-auth/core/oauth2"; +import "@better-auth/utils/otp"; +function checkHasPath(url) { + try { + return (new URL(url).pathname.replace(/\/+$/, "") || "/") !== "/"; + } catch { + throw new BetterAuthError(`Invalid base URL: ${url}. Please provide a valid base URL.`); + } +} +function assertHasProtocol(url) { + try { + const parsedUrl = new URL(url); + if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new BetterAuthError(`Invalid base URL: ${url}. URL must include 'http://' or 'https://'`); + } catch (error2) { + if (error2 instanceof BetterAuthError) throw error2; + throw new BetterAuthError(`Invalid base URL: ${url}. Please provide a valid base URL.`, { cause: error2 }); + } +} +function withPath(url, path = "/api/auth") { + assertHasProtocol(url); + if (checkHasPath(url)) return url; + const trimmedUrl = url.replace(/\/+$/, ""); + if (!path || path === "/") return trimmedUrl; + path = path.startsWith("/") ? path : `/${path}`; + return `${trimmedUrl}${path}`; +} +function validateProxyHeader(header, type) { + if (!header || header.trim() === "") return false; + if (type === "proto") return header === "http" || header === "https"; + if (type === "host") { + if ([ + /\.\./, + /\0/, + /[\s]/, + /^[.]/, + /[<>'"]/, + /javascript:/i, + /file:/i, + /data:/i + ].some((pattern) => pattern.test(header))) return false; + return /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*(:[0-9]{1,5})?$/.test(header) || /^(\d{1,3}\.){3}\d{1,3}(:[0-9]{1,5})?$/.test(header) || /^\[[0-9a-fA-F:]+\](:[0-9]{1,5})?$/.test(header) || /^localhost(:[0-9]{1,5})?$/i.test(header); + } + return false; +} +function getBaseURL(url, path, request, loadEnv, trustedProxyHeaders) { + if (url) return withPath(url, path); + { + const fromEnv = env.BETTER_AUTH_URL || env.NEXT_PUBLIC_BETTER_AUTH_URL || env.PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_AUTH_URL || (env.BASE_URL !== "/" ? env.BASE_URL : void 0); + if (fromEnv) return withPath(fromEnv, path); + } + const fromRequest = request?.headers.get("x-forwarded-host"); + const fromRequestProto = request?.headers.get("x-forwarded-proto"); + if (fromRequest && fromRequestProto && trustedProxyHeaders) { + if (validateProxyHeader(fromRequestProto, "proto") && validateProxyHeader(fromRequest, "host")) try { + return withPath(`${fromRequestProto}://${fromRequest}`, path); + } catch (_error) { + } + } + if (request) { + const url$1 = getOrigin(request.url); + if (!url$1) throw new BetterAuthError("Could not get origin from request. Please provide a valid base URL."); + return withPath(url$1, path); + } + if (typeof window !== "undefined" && window.location) return withPath(window.location.origin, path); +} +function getOrigin(url) { + try { + const parsedUrl = new URL(url); + return parsedUrl.origin === "null" ? null : parsedUrl.origin; + } catch { + return null; + } +} +function getProtocol(url) { + try { + return new URL(url).protocol; + } catch { + return null; + } +} +function getHost(url) { + try { + return new URL(url).host; + } catch { + return null; + } +} +const LOCALHOST_IP = "127.0.0.1"; +function getIp(req, options) { + if (options.advanced?.ipAddress?.disableIpTracking) return null; + const headers = "headers" in req ? req.headers : req; + const ipHeaders = options.advanced?.ipAddress?.ipAddressHeaders || ["x-forwarded-for"]; + for (const key of ipHeaders) { + const value = "get" in headers ? headers.get(key) : headers[key]; + if (typeof value === "string") { + const ip = value.split(",")[0].trim(); + if (isValidIP(ip)) return normalizeIP(ip, { ipv6Subnet: options.advanced?.ipAddress?.ipv6Subnet }); + } + } + if (isTest() || isDevelopment()) return LOCALHOST_IP; + return null; +} +const { get: getOAuthState, set: setOAuthState } = defineRequestState(() => null); +function escapeRegExpChar(char) { + if (char === "-" || char === "^" || char === "$" || char === "+" || char === "." || char === "(" || char === ")" || char === "|" || char === "[" || char === "]" || char === "{" || char === "}" || char === "*" || char === "?" || char === "\\") return `\\${char}`; + else return char; +} +function escapeRegExpString(str) { + let result = ""; + for (let i = 0; i < str.length; i++) result += escapeRegExpChar(str[i]); + return result; +} +function transform(pattern, separator = true) { + if (Array.isArray(pattern)) return `(?:${pattern.map((p) => `^${transform(p, separator)}$`).join("|")})`; + let separatorSplitter = ""; + let separatorMatcher = ""; + let wildcard = "."; + if (separator === true) { + separatorSplitter = "/"; + separatorMatcher = "[/\\\\]"; + wildcard = "[^/\\\\]"; + } else if (separator) { + separatorSplitter = separator; + separatorMatcher = escapeRegExpString(separatorSplitter); + if (separatorMatcher.length > 1) { + separatorMatcher = `(?:${separatorMatcher})`; + wildcard = `((?!${separatorMatcher}).)`; + } else wildcard = `[^${separatorMatcher}]`; + } + const requiredSeparator = separator ? `${separatorMatcher}+?` : ""; + const optionalSeparator = separator ? `${separatorMatcher}*?` : ""; + const segments = separator ? pattern.split(separatorSplitter) : [pattern]; + let result = ""; + for (let s = 0; s < segments.length; s++) { + const segment = segments[s]; + const nextSegment = segments[s + 1]; + let currentSeparator = ""; + if (!segment && s > 0) continue; + if (separator) if (s === segments.length - 1) currentSeparator = optionalSeparator; + else if (nextSegment !== "**") currentSeparator = requiredSeparator; + else currentSeparator = ""; + if (separator && segment === "**") { + if (currentSeparator) { + result += s === 0 ? "" : currentSeparator; + result += `(?:${wildcard}*?${currentSeparator})*?`; + } + continue; + } + for (let c = 0; c < segment.length; c++) { + const char = segment[c]; + if (char === "\\") { + if (c < segment.length - 1) { + result += escapeRegExpChar(segment[c + 1]); + c++; + } + } else if (char === "?") result += wildcard; + else if (char === "*") result += `${wildcard}*?`; + else result += escapeRegExpChar(char); + } + result += currentSeparator; + } + return result; +} +function isMatch(regexp, sample) { + if (typeof sample !== "string") throw new TypeError(`Sample must be a string, but ${typeof sample} given`); + return regexp.test(sample); +} +function wildcardMatch(pattern, options) { + if (typeof pattern !== "string" && !Array.isArray(pattern)) throw new TypeError(`The first argument must be a single pattern string or an array of patterns, but ${typeof pattern} given`); + if (typeof options === "string" || typeof options === "boolean") options = { separator: options }; + if (arguments.length === 2 && !(typeof options === "undefined" || typeof options === "object" && options !== null && !Array.isArray(options))) throw new TypeError(`The second argument must be an options object or a string/boolean separator, but ${typeof options} given`); + options = options || {}; + if (options.separator === "\\") throw new Error("\\ is not a valid separator because it is used for escaping. Try setting the separator to `true` instead"); + const regexpPattern = transform(pattern, options.separator); + const regexp = new RegExp(`^${regexpPattern}$`, options.flags); + const fn = isMatch.bind(null, regexp); + fn.options = options; + fn.pattern = pattern; + fn.regexp = regexp; + return fn; +} +const matchesOriginPattern = (url, pattern, settings) => { + if (url.startsWith("/")) { + if (settings?.allowRelativePaths) return url.startsWith("/") && /^\/(?!\/|\\|%2f|%5c)[\w\-.\+/@]*(?:\?[\w\-.\+/=&%@]*)?$/.test(url); + return false; + } + if (pattern.includes("*") || pattern.includes("?")) { + if (pattern.includes("://")) return wildcardMatch(pattern)(getOrigin(url) || url); + const host = getHost(url); + if (!host) return false; + return wildcardMatch(pattern)(host); + } + const protocol = getProtocol(url); + return protocol === "http:" || protocol === "https:" || !protocol ? pattern === getOrigin(url) : url.startsWith(pattern); +}; +function shouldSkipCSRFForBackwardCompat(ctx) { + return ctx.context.skipOriginCheck === true && ctx.context.options.advanced?.disableCSRFCheck === void 0; +} +const logBackwardCompatWarning = deprecate(function logBackwardCompatWarning$1() { +}, "disableOriginCheck: true currently also disables CSRF checks. In a future version, disableOriginCheck will ONLY disable URL validation. To keep CSRF disabled, add disableCSRFCheck: true to your config."); +const originCheckMiddleware = createAuthMiddleware(async (ctx) => { + if (ctx.request?.method === "GET" || ctx.request?.method === "OPTIONS" || ctx.request?.method === "HEAD" || !ctx.request) return; + await validateOrigin(ctx); + if (ctx.context.skipOriginCheck) return; + const { body, query } = ctx; + const callbackURL = body?.callbackURL || query?.callbackURL; + const redirectURL = body?.redirectTo; + const errorCallbackURL = body?.errorCallbackURL; + const newUserCallbackURL = body?.newUserCallbackURL; + const validateURL = (url, label) => { + if (!url) return; + if (!ctx.context.isTrustedOrigin(url, { allowRelativePaths: label !== "origin" })) { + ctx.context.logger.error(`Invalid ${label}: ${url}`); + ctx.context.logger.info(`If it's a valid URL, please add ${url} to trustedOrigins in your auth config +`, `Current list of trustedOrigins: ${ctx.context.trustedOrigins}`); + throw new APIError("FORBIDDEN", { message: `Invalid ${label}` }); + } + }; + callbackURL && validateURL(callbackURL, "callbackURL"); + redirectURL && validateURL(redirectURL, "redirectURL"); + errorCallbackURL && validateURL(errorCallbackURL, "errorCallbackURL"); + newUserCallbackURL && validateURL(newUserCallbackURL, "newUserCallbackURL"); +}); +const originCheck = (getValue) => createAuthMiddleware(async (ctx) => { + if (!ctx.request) return; + if (ctx.context.skipOriginCheck) return; + const callbackURL = getValue(ctx); + const validateURL = (url, label) => { + if (!url) return; + if (!ctx.context.isTrustedOrigin(url, { allowRelativePaths: label !== "origin" })) { + ctx.context.logger.error(`Invalid ${label}: ${url}`); + ctx.context.logger.info(`If it's a valid URL, please add ${url} to trustedOrigins in your auth config +`, `Current list of trustedOrigins: ${ctx.context.trustedOrigins}`); + throw new APIError("FORBIDDEN", { message: `Invalid ${label}` }); + } + }; + const callbacks = Array.isArray(callbackURL) ? callbackURL : [callbackURL]; + for (const url of callbacks) validateURL(url, "callbackURL"); +}); +async function validateOrigin(ctx, forceValidate = false) { + const headers = ctx.request?.headers; + if (!headers || !ctx.request) return; + const originHeader = headers.get("origin") || headers.get("referer") || ""; + const useCookies = headers.has("cookie"); + if (ctx.context.skipCSRFCheck) return; + if (shouldSkipCSRFForBackwardCompat(ctx)) { + ctx.context.options.advanced?.disableOriginCheck === true && logBackwardCompatWarning(); + return; + } + const skipOriginCheck = ctx.context.skipOriginCheck; + if (Array.isArray(skipOriginCheck)) try { + const basePath = new URL(ctx.context.baseURL).pathname; + const currentPath = normalizePathname(ctx.request.url, basePath); + if (skipOriginCheck.some((skipPath) => currentPath.startsWith(skipPath))) return; + } catch { + } + if (!(forceValidate || useCookies)) return; + if (!originHeader || originHeader === "null") throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.MISSING_OR_NULL_ORIGIN }); + const trustedOrigins = Array.isArray(ctx.context.options.trustedOrigins) ? ctx.context.trustedOrigins : [...ctx.context.trustedOrigins, ...(await ctx.context.options.trustedOrigins?.(ctx.request))?.filter((v) => Boolean(v)) || []]; + if (!trustedOrigins.some((origin) => matchesOriginPattern(originHeader, origin))) { + ctx.context.logger.error(`Invalid origin: ${originHeader}`); + ctx.context.logger.info(`If it's a valid URL, please add ${originHeader} to trustedOrigins in your auth config +`, `Current list of trustedOrigins: ${trustedOrigins}`); + throw new APIError("FORBIDDEN", { message: "Invalid origin" }); + } +} +const formCsrfMiddleware = createAuthMiddleware(async (ctx) => { + if (!ctx.request) return; + await validateFormCsrf(ctx); +}); +async function validateFormCsrf(ctx) { + const req = ctx.request; + if (!req) return; + if (ctx.context.skipCSRFCheck) return; + if (shouldSkipCSRFForBackwardCompat(ctx)) return; + const headers = req.headers; + if (headers.has("cookie")) return await validateOrigin(ctx); + const site = headers.get("Sec-Fetch-Site"); + const mode = headers.get("Sec-Fetch-Mode"); + const dest = headers.get("Sec-Fetch-Dest"); + if (Boolean(site && site.trim() || mode && mode.trim() || dest && dest.trim())) { + if (site === "cross-site" && mode === "navigate") { + ctx.context.logger.error("Blocked cross-site navigation login attempt (CSRF protection)", { + secFetchSite: site, + secFetchMode: mode, + secFetchDest: dest + }); + throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.CROSS_SITE_NAVIGATION_LOGIN_BLOCKED }); + } + return await validateOrigin(ctx, true); + } +} +const memory = /* @__PURE__ */ new Map(); +function shouldRateLimit(max, window2, rateLimitData) { + const now2 = Date.now(); + const windowInMs = window2 * 1e3; + return now2 - rateLimitData.lastRequest < windowInMs && rateLimitData.count >= max; +} +function rateLimitResponse(retryAfter) { + return new Response(JSON.stringify({ message: "Too many requests. Please try again later." }), { + status: 429, + statusText: "Too Many Requests", + headers: { "X-Retry-After": retryAfter.toString() } + }); +} +function getRetryAfter(lastRequest, window2) { + const now2 = Date.now(); + const windowInMs = window2 * 1e3; + return Math.ceil((lastRequest + windowInMs - now2) / 1e3); +} +function createDatabaseStorageWrapper(ctx) { + const model = "rateLimit"; + const db2 = ctx.adapter; + return { + get: async (key) => { + const data = (await db2.findMany({ + model, + where: [{ + field: "key", + value: key + }] + }))[0]; + if (typeof data?.lastRequest === "bigint") data.lastRequest = Number(data.lastRequest); + return data; + }, + set: async (key, value, _update) => { + try { + if (_update) await db2.updateMany({ + model, + where: [{ + field: "key", + value: key + }], + update: { + count: value.count, + lastRequest: value.lastRequest + } + }); + else await db2.create({ + model, + data: { + key, + count: value.count, + lastRequest: value.lastRequest + } + }); + } catch (e) { + ctx.logger.error("Error setting rate limit", e); + } + } + }; +} +function getRateLimitStorage(ctx, rateLimitSettings) { + if (ctx.options.rateLimit?.customStorage) return ctx.options.rateLimit.customStorage; + const storage = ctx.rateLimit.storage; + if (storage === "secondary-storage") return { + get: async (key) => { + const data = await ctx.options.secondaryStorage?.get(key); + return data ? safeJSONParse(data) : null; + }, + set: async (key, value, _update) => { + const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10; + await ctx.options.secondaryStorage?.set?.(key, JSON.stringify(value), ttl); + } + }; + else if (storage === "memory") return { + async get(key) { + const entry = memory.get(key); + if (!entry) return null; + if (Date.now() >= entry.expiresAt) { + memory.delete(key); + return null; + } + return entry.data; + }, + async set(key, value, _update) { + const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10; + const expiresAt = Date.now() + ttl * 1e3; + memory.set(key, { + data: value, + expiresAt + }); + } + }; + return createDatabaseStorageWrapper(ctx); +} +async function onRequestRateLimit(req, ctx) { + if (!ctx.rateLimit.enabled) return; + const basePath = new URL(ctx.baseURL).pathname; + const path = normalizePathname(req.url, basePath); + let currentWindow = ctx.rateLimit.window; + let currentMax = ctx.rateLimit.max; + const ip = getIp(req, ctx.options); + if (!ip) return; + const key = createRateLimitKey(ip, path); + const specialRule = getDefaultSpecialRules().find((rule) => rule.pathMatcher(path)); + if (specialRule) { + currentWindow = specialRule.window; + currentMax = specialRule.max; + } + for (const plugin of ctx.options.plugins || []) if (plugin.rateLimit) { + const matchedRule = plugin.rateLimit.find((rule) => rule.pathMatcher(path)); + if (matchedRule) { + currentWindow = matchedRule.window; + currentMax = matchedRule.max; + break; + } + } + if (ctx.rateLimit.customRules) { + const _path = Object.keys(ctx.rateLimit.customRules).find((p) => { + if (p.includes("*")) return wildcardMatch(p)(path); + return p === path; + }); + if (_path) { + const customRule = ctx.rateLimit.customRules[_path]; + const resolved = typeof customRule === "function" ? await customRule(req, { + window: currentWindow, + max: currentMax + }) : customRule; + if (resolved) { + currentWindow = resolved.window; + currentMax = resolved.max; + } + if (resolved === false) return; + } + } + const storage = getRateLimitStorage(ctx, { window: currentWindow }); + const data = await storage.get(key); + const now2 = Date.now(); + if (!data) await storage.set(key, { + key, + count: 1, + lastRequest: now2 + }); + else { + const timeSinceLastRequest = now2 - data.lastRequest; + if (shouldRateLimit(currentMax, currentWindow, data)) return rateLimitResponse(getRetryAfter(data.lastRequest, currentWindow)); + else if (timeSinceLastRequest > currentWindow * 1e3) await storage.set(key, { + ...data, + count: 1, + lastRequest: now2 + }, true); + else await storage.set(key, { + ...data, + count: data.count + 1, + lastRequest: now2 + }, true); + } +} +function getDefaultSpecialRules() { + return [{ + pathMatcher(path) { + return path.startsWith("/sign-in") || path.startsWith("/sign-up") || path.startsWith("/change-password") || path.startsWith("/change-email"); + }, + window: 10, + max: 3 + }]; +} +const getDate = (span, unit = "ms") => { + return new Date(Date.now() + (unit === "sec" ? span * 1e3 : span)); +}; +const cache = /* @__PURE__ */ new WeakMap(); +function parseOutputData(data, schema2) { + const fields = schema2.fields; + const parsedData = {}; + for (const key in data) { + const field = fields[key]; + if (!field) { + parsedData[key] = data[key]; + continue; + } + if (field.returned === false && key !== "id") continue; + parsedData[key] = data[key]; + } + return parsedData; +} +function getFields(options, table, mode) { + const cacheKey = `${table}:${mode}`; + if (!cache.has(options)) cache.set(options, /* @__PURE__ */ new Map()); + const tableCache = cache.get(options); + if (tableCache.has(cacheKey)) return tableCache.get(cacheKey); + const coreSchema = mode === "output" ? getAuthTables(options)[table]?.fields ?? {} : {}; + const additionalFields = table === "user" || table === "session" || table === "account" ? options[table]?.additionalFields : void 0; + let schema2 = { + ...coreSchema, + ...additionalFields ?? {} + }; + for (const plugin of options.plugins || []) if (plugin.schema && plugin.schema[table]) schema2 = { + ...schema2, + ...plugin.schema[table].fields + }; + tableCache.set(cacheKey, schema2); + return schema2; +} +function parseUserOutput(options, user) { + return parseOutputData(user, { fields: getFields(options, "user", "output") }); +} +function parseSessionOutput(options, session) { + return parseOutputData(session, { fields: getFields(options, "session", "output") }); +} +function parseAccountOutput(options, account) { + const { accessToken: _accessToken, refreshToken: _refreshToken, idToken: _idToken, accessTokenExpiresAt: _accessTokenExpiresAt, refreshTokenExpiresAt: _refreshTokenExpiresAt, password: _password, ...rest } = parseOutputData(account, { fields: getFields(options, "account", "output") }); + return rest; +} +function parseInputData(data, schema2) { + const action = schema2.action || "create"; + const fields = schema2.fields; + const parsedData = Object.assign(/* @__PURE__ */ Object.create(null), null); + for (const key in fields) { + if (key in data) { + if (fields[key].input === false) { + if (fields[key].defaultValue !== void 0) { + if (action !== "update") { + parsedData[key] = fields[key].defaultValue; + continue; + } + } + if (data[key]) throw new APIError("BAD_REQUEST", { message: `${key} is not allowed to be set` }); + continue; + } + if (fields[key].validator?.input && data[key] !== void 0) { + const result = fields[key].validator.input["~standard"].validate(data[key]); + if (result instanceof Promise) throw new APIError("INTERNAL_SERVER_ERROR", { message: "Async validation is not supported for additional fields" }); + if ("issues" in result && result.issues) throw new APIError("BAD_REQUEST", { message: result.issues[0]?.message || "Validation Error" }); + parsedData[key] = result.value; + continue; + } + if (fields[key].transform?.input && data[key] !== void 0) { + parsedData[key] = fields[key].transform?.input(data[key]); + continue; + } + parsedData[key] = data[key]; + continue; + } + if (fields[key].defaultValue !== void 0 && action === "create") { + if (typeof fields[key].defaultValue === "function") { + parsedData[key] = fields[key].defaultValue(); + continue; + } + parsedData[key] = fields[key].defaultValue; + continue; + } + if (fields[key].required && action === "create") throw new APIError("BAD_REQUEST", { message: `${key} is required` }); + } + return parsedData; +} +function parseUserInput(options, user = {}, action) { + return parseInputData(user, { + fields: getFields(options, "user", "input"), + action + }); +} +function parseAdditionalUserInput(options, user) { + const schema2 = getFields(options, "user", "input"); + return parseInputData(user || {}, { fields: schema2 }); +} +function parseAccountInput(options, account) { + return parseInputData(account, { fields: getFields(options, "account", "input") }); +} +function parseSessionInput(options, session) { + return parseInputData(session, { fields: getFields(options, "session", "input") }); +} +function mergeSchema(schema2, newSchema) { + if (!newSchema) return schema2; + for (const table in newSchema) { + const newModelName = newSchema[table]?.modelName; + if (newModelName) schema2[table].modelName = newModelName; + for (const field in schema2[table].fields) { + const newField = newSchema[table]?.fields?.[field]; + if (!newField) continue; + schema2[table].fields[field].fieldName = newField; + } + } + return schema2; +} +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (all, symbols) => { + let target = {}; + for (var name in all) { + __defProp(target, name, { + get: all[name], + enumerable: true + }); + } + return target; +}; +var __copyProps = (to, from, except, desc2) => { + if (from && typeof from === "object" || typeof from === "function") { + for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { + key = keys[i]; + if (!__hasOwnProp.call(to, key) && key !== except) { + __defProp(to, key, { + get: ((k) => from[k]).bind(null, key), + enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable + }); + } + } + } + return to; +}; +var __reExport = (target, mod, secondTarget, symbols) => { + __copyProps(target, mod, "default"); +}; +async function getBaseAdapter(options, handleDirectDatabase) { + let adapter; + if (!options.database) { + const tables = getAuthTables(options); + const memoryDB = Object.keys(tables).reduce((acc, key) => { + acc[key] = []; + return acc; + }, {}); + const { memoryAdapter } = await import("./index5.js"); + adapter = memoryAdapter(memoryDB)(options); + } else if (typeof options.database === "function") adapter = options.database(options); + else adapter = await handleDirectDatabase(options); + if (!adapter.transaction) { + logger.warn("Adapter does not correctly implement transaction function, patching it automatically. Please update your adapter implementation."); + adapter.transaction = async (cb) => { + return cb(adapter); + }; + } + return adapter; +} +async function getAdapter(options) { + return getBaseAdapter(options, async (opts) => { + const { createKyselyAdapter: createKyselyAdapter2 } = await import("./index6.js"); + const { kysely, databaseType, transaction } = await createKyselyAdapter2(opts); + if (!kysely) throw new BetterAuthError("Failed to initialize database adapter"); + const { kyselyAdapter } = await import("./index6.js"); + return kyselyAdapter(kysely, { + type: databaseType || "sqlite", + debugLogs: opts.database && "debugLogs" in opts.database ? opts.database.debugLogs : false, + transaction + })(opts); + }); +} +const createFieldAttribute = (type, config2) => { + return { + type, + ...config2 + }; +}; +function convertToDB(fields, values) { + const result = values.id ? { id: values.id } : {}; + for (const key in fields) { + const field = fields[key]; + const value = values[key]; + if (value === void 0) continue; + result[field.fieldName || key] = value; + } + return result; +} +function convertFromDB(fields, values) { + if (!values) return null; + const result = { id: values.id }; + for (const [key, value] of Object.entries(fields)) result[key] = values[value.fieldName || key]; + return result; +} +function getWithHooks(adapter, ctx) { + const hooks = ctx.hooks; + async function createWithHooks(data, model, customCreateFn) { + const context = await getCurrentAuthContext().catch(() => null); + let actualData = data; + for (const hook of hooks || []) { + const toRun = hook[model]?.create?.before; + if (toRun) { + const result = await toRun(actualData, context); + if (result === false) return null; + if (typeof result === "object" && "data" in result) actualData = { + ...actualData, + ...result.data + }; + } + } + const customCreated = customCreateFn ? await customCreateFn.fn(actualData) : null; + const created = !customCreateFn || customCreateFn.executeMainFn ? await (await getCurrentAdapter(adapter)).create({ + model, + data: actualData, + forceAllowId: true + }) : customCreated; + for (const hook of hooks || []) { + const toRun = hook[model]?.create?.after; + if (toRun) await toRun(created, context); + } + return created; + } + async function updateWithHooks(data, where, model, customUpdateFn) { + const context = await getCurrentAuthContext().catch(() => null); + let actualData = data; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.before; + if (toRun) { + const result = await toRun(data, context); + if (result === false) return null; + if (typeof result === "object" && "data" in result) actualData = { + ...actualData, + ...result.data + }; + } + } + const customUpdated = customUpdateFn ? await customUpdateFn.fn(actualData) : null; + const updated = !customUpdateFn || customUpdateFn.executeMainFn ? await (await getCurrentAdapter(adapter)).update({ + model, + update: actualData, + where + }) : customUpdated; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.after; + if (toRun) await toRun(updated, context); + } + return updated; + } + async function updateManyWithHooks(data, where, model, customUpdateFn) { + const context = await getCurrentAuthContext().catch(() => null); + let actualData = data; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.before; + if (toRun) { + const result = await toRun(data, context); + if (result === false) return null; + if (typeof result === "object" && "data" in result) actualData = { + ...actualData, + ...result.data + }; + } + } + const customUpdated = customUpdateFn ? await customUpdateFn.fn(actualData) : null; + const updated = !customUpdateFn || customUpdateFn.executeMainFn ? await (await getCurrentAdapter(adapter)).updateMany({ + model, + update: actualData, + where + }) : customUpdated; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.after; + if (toRun) await toRun(updated, context); + } + return updated; + } + async function deleteWithHooks(where, model, customDeleteFn) { + const context = await getCurrentAuthContext().catch(() => null); + let entityToDelete = null; + try { + entityToDelete = (await (await getCurrentAdapter(adapter)).findMany({ + model, + where, + limit: 1 + }))[0] || null; + } catch { + } + if (entityToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.before; + if (toRun) { + if (await toRun(entityToDelete, context) === false) return null; + } + } + const customDeleted = customDeleteFn ? await customDeleteFn.fn(where) : null; + const deleted = !customDeleteFn || customDeleteFn.executeMainFn ? await (await getCurrentAdapter(adapter)).delete({ + model, + where + }) : customDeleted; + if (entityToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.after; + if (toRun) await toRun(entityToDelete, context); + } + return deleted; + } + async function deleteManyWithHooks(where, model, customDeleteFn) { + const context = await getCurrentAuthContext().catch(() => null); + let entitiesToDelete = []; + try { + entitiesToDelete = await (await getCurrentAdapter(adapter)).findMany({ + model, + where + }); + } catch { + } + for (const entity of entitiesToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.before; + if (toRun) { + if (await toRun(entity, context) === false) return null; + } + } + const customDeleted = customDeleteFn ? await customDeleteFn.fn(where) : null; + const deleted = !customDeleteFn || customDeleteFn.executeMainFn ? await (await getCurrentAdapter(adapter)).deleteMany({ + model, + where + }) : customDeleted; + for (const entity of entitiesToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.after; + if (toRun) await toRun(entity, context); + } + return deleted; + } + return { + createWithHooks, + updateWithHooks, + updateManyWithHooks, + deleteWithHooks, + deleteManyWithHooks + }; +} +const createInternalAdapter = (adapter, ctx) => { + const logger2 = ctx.logger; + const options = ctx.options; + const secondaryStorage = options.secondaryStorage; + const sessionExpiration = options.session?.expiresIn || 3600 * 24 * 7; + const { createWithHooks, updateWithHooks, updateManyWithHooks, deleteWithHooks, deleteManyWithHooks } = getWithHooks(adapter, ctx); + async function refreshUserSessions(user) { + if (!secondaryStorage) return; + const listRaw = await secondaryStorage.get(`active-sessions-${user.id}`); + if (!listRaw) return; + const now2 = Date.now(); + const validSessions = (safeJSONParse(listRaw) || []).filter((s) => s.expiresAt > now2); + await Promise.all(validSessions.map(async ({ token }) => { + const cached = await secondaryStorage.get(token); + if (!cached) return; + const parsed = safeJSONParse(cached); + if (!parsed) return; + const sessionTTL = Math.max(Math.floor(new Date(parsed.session.expiresAt).getTime() - now2) / 1e3, 0); + await secondaryStorage.set(token, JSON.stringify({ + session: parsed.session, + user + }), Math.floor(sessionTTL)); + })); + } + return { + createOAuthUser: async (user, account) => { + return runWithTransaction(adapter, async () => { + const createdUser = await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...user + }, "user", void 0); + return { + user: createdUser, + account: await createWithHooks({ + ...account, + userId: createdUser.id, + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date() + }, "account", void 0) + }; + }); + }, + createUser: async (user) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...user, + email: user.email?.toLowerCase() + }, "user", void 0); + }, + createAccount: async (account) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...account + }, "account", void 0); + }, + listSessions: async (userId) => { + if (secondaryStorage) { + const currentList = await secondaryStorage.get(`active-sessions-${userId}`); + if (!currentList) return []; + const list = safeJSONParse(currentList) || []; + const now2 = Date.now(); + const seenTokens = /* @__PURE__ */ new Set(); + const sessions = []; + for (const { token, expiresAt } of list) { + if (expiresAt <= now2 || seenTokens.has(token)) continue; + seenTokens.add(token); + const data = await secondaryStorage.get(token); + if (!data) continue; + try { + const parsed = typeof data === "string" ? JSON.parse(data) : data; + if (!parsed?.session) continue; + sessions.push(parseSessionOutput(ctx.options, { + ...parsed.session, + expiresAt: new Date(parsed.session.expiresAt) + })); + } catch { + continue; + } + } + return sessions; + } + return await (await getCurrentAdapter(adapter)).findMany({ + model: "session", + where: [{ + field: "userId", + value: userId + }] + }); + }, + listUsers: async (limit, offset, sortBy, where) => { + return await (await getCurrentAdapter(adapter)).findMany({ + model: "user", + limit, + offset, + sortBy, + where + }); + }, + countTotalUsers: async (where) => { + const total = await (await getCurrentAdapter(adapter)).count({ + model: "user", + where + }); + if (typeof total === "string") return parseInt(total); + return total; + }, + deleteUser: async (userId) => { + if (!secondaryStorage || options.session?.storeSessionInDatabase) await deleteManyWithHooks([{ + field: "userId", + value: userId + }], "session", void 0); + await deleteManyWithHooks([{ + field: "userId", + value: userId + }], "account", void 0); + await deleteWithHooks([{ + field: "id", + value: userId + }], "user", void 0); + }, + createSession: async (userId, dontRememberMe, override, overrideAll) => { + const ctx$1 = await getCurrentAuthContext().catch(() => null); + const headers = ctx$1?.headers || ctx$1?.request?.headers; + const { id: _, ...rest } = override || {}; + const defaultAdditionalFields = parseSessionInput(ctx$1?.context.options ?? options, {}); + const data = { + ipAddress: ctx$1?.request || ctx$1?.headers ? getIp(ctx$1?.request || ctx$1?.headers, ctx$1?.context.options) || "" : "", + userAgent: headers?.get("user-agent") || "", + ...rest, + expiresAt: dontRememberMe ? getDate(3600 * 24, "sec") : getDate(sessionExpiration, "sec"), + userId, + token: generateId(32), + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...defaultAdditionalFields, + ...overrideAll ? rest : {} + }; + return await createWithHooks(data, "session", secondaryStorage ? { + fn: async (sessionData) => { + const currentList = await secondaryStorage.get(`active-sessions-${userId}`); + let list = []; + const now2 = Date.now(); + if (currentList) { + list = safeJSONParse(currentList) || []; + list = list.filter((session) => session.expiresAt > now2 && session.token !== data.token); + } + const sorted = [...list, { + token: data.token, + expiresAt: data.expiresAt.getTime() + }].sort((a, b) => a.expiresAt - b.expiresAt); + const furthestSessionExp = sorted.at(-1)?.expiresAt ?? data.expiresAt.getTime(); + const furthestSessionTTL = Math.max(Math.floor((furthestSessionExp - now2) / 1e3), 0); + if (furthestSessionTTL > 0) await secondaryStorage.set(`active-sessions-${userId}`, JSON.stringify(sorted), furthestSessionTTL); + const user = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + field: "id", + value: userId + }] + }); + const sessionTTL = Math.max(Math.floor((data.expiresAt.getTime() - now2) / 1e3), 0); + if (sessionTTL > 0) await secondaryStorage.set(data.token, JSON.stringify({ + session: sessionData, + user + }), sessionTTL); + return sessionData; + }, + executeMainFn: options.session?.storeSessionInDatabase + } : void 0); + }, + findSession: async (token) => { + if (secondaryStorage) { + const sessionStringified = await secondaryStorage.get(token); + if (!sessionStringified && !options.session?.storeSessionInDatabase) return null; + if (sessionStringified) { + const s = safeJSONParse(sessionStringified); + if (!s) return null; + return { + session: parseSessionOutput(ctx.options, { + ...s.session, + expiresAt: new Date(s.session.expiresAt), + createdAt: new Date(s.session.createdAt), + updatedAt: new Date(s.session.updatedAt) + }), + user: parseUserOutput(ctx.options, { + ...s.user, + createdAt: new Date(s.user.createdAt), + updatedAt: new Date(s.user.updatedAt) + }) + }; + } + } + const result = await (await getCurrentAdapter(adapter)).findOne({ + model: "session", + where: [{ + value: token, + field: "token" + }], + join: { user: true } + }); + if (!result) return null; + const { user, ...session } = result; + if (!user) return null; + return { + session: parseSessionOutput(ctx.options, session), + user: parseUserOutput(ctx.options, user) + }; + }, + findSessions: async (sessionTokens) => { + if (secondaryStorage) { + const sessions$1 = []; + for (const sessionToken of sessionTokens) { + const sessionStringified = await secondaryStorage.get(sessionToken); + if (sessionStringified) try { + const s = typeof sessionStringified === "string" ? JSON.parse(sessionStringified) : sessionStringified; + if (!s?.session) continue; + const session = { + session: { + ...s.session, + expiresAt: new Date(s.session.expiresAt) + }, + user: { + ...s.user, + createdAt: new Date(s.user.createdAt), + updatedAt: new Date(s.user.updatedAt) + } + }; + sessions$1.push(session); + } catch { + continue; + } + } + return sessions$1; + } + const sessions = await (await getCurrentAdapter(adapter)).findMany({ + model: "session", + where: [{ + field: "token", + value: sessionTokens, + operator: "in" + }], + join: { user: true } + }); + if (!sessions.length) return []; + if (sessions.some((session) => !session.user)) return []; + return sessions.map((_session) => { + const { user, ...session } = _session; + return { + session, + user + }; + }); + }, + updateSession: async (sessionToken, session) => { + return await updateWithHooks(session, [{ + field: "token", + value: sessionToken + }], "session", secondaryStorage ? { + async fn(data) { + const currentSession = await secondaryStorage.get(sessionToken); + if (!currentSession) return null; + const parsedSession = safeJSONParse(currentSession); + if (!parsedSession) return null; + const mergedSession = { + ...parsedSession.session, + ...data, + expiresAt: new Date(data.expiresAt ?? parsedSession.session.expiresAt), + createdAt: new Date(parsedSession.session.createdAt), + updatedAt: new Date(data.updatedAt ?? parsedSession.session.updatedAt) + }; + const updatedSession = parseSessionOutput(ctx.options, mergedSession); + const now2 = Date.now(); + const expiresMs = new Date(updatedSession.expiresAt).getTime(); + const sessionTTL = Math.max(Math.floor((expiresMs - now2) / 1e3), 0); + if (sessionTTL > 0) { + await secondaryStorage.set(sessionToken, JSON.stringify({ + session: updatedSession, + user: parsedSession.user + }), sessionTTL); + const listKey = `active-sessions-${updatedSession.userId}`; + const listRaw = await secondaryStorage.get(listKey); + const sorted = (listRaw ? safeJSONParse(listRaw) || [] : []).filter((s) => s.token !== sessionToken && s.expiresAt > now2).concat([{ + token: sessionToken, + expiresAt: expiresMs + }]).sort((a, b) => a.expiresAt - b.expiresAt); + const furthestSessionExp = sorted.at(-1)?.expiresAt; + if (furthestSessionExp && furthestSessionExp > now2) await secondaryStorage.set(listKey, JSON.stringify(sorted), Math.floor((furthestSessionExp - now2) / 1e3)); + else await secondaryStorage.delete(listKey); + } + return updatedSession; + }, + executeMainFn: options.session?.storeSessionInDatabase + } : void 0); + }, + deleteSession: async (token) => { + if (secondaryStorage) { + const data = await secondaryStorage.get(token); + if (data) { + const { session } = safeJSONParse(data) ?? {}; + if (!session) { + logger2.error("Session not found in secondary storage"); + return; + } + const userId = session.userId; + const currentList = await secondaryStorage.get(`active-sessions-${userId}`); + if (currentList) { + const list = safeJSONParse(currentList) || []; + const now2 = Date.now(); + const filtered = list.filter((session$1) => session$1.expiresAt > now2 && session$1.token !== token); + const furthestSessionExp = filtered.sort((a, b) => a.expiresAt - b.expiresAt).at(-1)?.expiresAt; + if (filtered.length > 0 && furthestSessionExp && furthestSessionExp > Date.now()) await secondaryStorage.set(`active-sessions-${userId}`, JSON.stringify(filtered), Math.floor((furthestSessionExp - now2) / 1e3)); + else await secondaryStorage.delete(`active-sessions-${userId}`); + } else logger2.error("Active sessions list not found in secondary storage"); + } + await secondaryStorage.delete(token); + if (!options.session?.storeSessionInDatabase || ctx.options.session?.preserveSessionInDatabase) return; + } + await deleteWithHooks([{ + field: "token", + value: token + }], "session", void 0); + }, + deleteAccounts: async (userId) => { + await deleteManyWithHooks([{ + field: "userId", + value: userId + }], "account", void 0); + }, + deleteAccount: async (accountId) => { + await deleteWithHooks([{ + field: "id", + value: accountId + }], "account", void 0); + }, + deleteSessions: async (userIdOrSessionTokens) => { + if (secondaryStorage) { + if (typeof userIdOrSessionTokens === "string") { + const activeSession = await secondaryStorage.get(`active-sessions-${userIdOrSessionTokens}`); + const sessions = activeSession ? safeJSONParse(activeSession) : []; + if (!sessions) return; + for (const session of sessions) await secondaryStorage.delete(session.token); + await secondaryStorage.delete(`active-sessions-${userIdOrSessionTokens}`); + } else for (const sessionToken of userIdOrSessionTokens) if (await secondaryStorage.get(sessionToken)) await secondaryStorage.delete(sessionToken); + if (!options.session?.storeSessionInDatabase || ctx.options.session?.preserveSessionInDatabase) return; + } + await deleteManyWithHooks([{ + field: Array.isArray(userIdOrSessionTokens) ? "token" : "userId", + value: userIdOrSessionTokens, + operator: Array.isArray(userIdOrSessionTokens) ? "in" : void 0 + }], "session", void 0); + }, + findOAuthUser: async (email, accountId, providerId) => { + const account = await (await getCurrentAdapter(adapter)).findOne({ + model: "account", + where: [{ + value: accountId, + field: "accountId" + }, { + value: providerId, + field: "providerId" + }], + join: { user: true } + }); + if (account) if (account.user) return { + user: account.user, + linkedAccount: account, + accounts: [account] + }; + else { + const user = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + value: email.toLowerCase(), + field: "email" + }] + }); + if (user) return { + user, + linkedAccount: account, + accounts: [account] + }; + return null; + } + else { + const user = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + value: email.toLowerCase(), + field: "email" + }] + }); + if (user) return { + user, + linkedAccount: null, + accounts: await (await getCurrentAdapter(adapter)).findMany({ + model: "account", + where: [{ + value: user.id, + field: "userId" + }] + }) || [] + }; + else return null; + } + }, + findUserByEmail: async (email, options$1) => { + const result = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + value: email.toLowerCase(), + field: "email" + }], + join: { ...options$1?.includeAccounts ? { account: true } : {} } + }); + if (!result) return null; + const { account: accounts, ...user } = result; + return { + user, + accounts: accounts ?? [] + }; + }, + findUserById: async (userId) => { + if (!userId) return null; + return await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + field: "id", + value: userId + }] + }); + }, + linkAccount: async (account) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...account + }, "account", void 0); + }, + updateUser: async (userId, data) => { + const user = await updateWithHooks(data, [{ + field: "id", + value: userId + }], "user", void 0); + await refreshUserSessions(user); + return user; + }, + updateUserByEmail: async (email, data) => { + const user = await updateWithHooks(data, [{ + field: "email", + value: email.toLowerCase() + }], "user", void 0); + await refreshUserSessions(user); + return user; + }, + updatePassword: async (userId, password) => { + await updateManyWithHooks({ password }, [{ + field: "userId", + value: userId + }, { + field: "providerId", + value: "credential" + }], "account", void 0); + }, + findAccounts: async (userId) => { + return await (await getCurrentAdapter(adapter)).findMany({ + model: "account", + where: [{ + field: "userId", + value: userId + }] + }); + }, + findAccount: async (accountId) => { + return await (await getCurrentAdapter(adapter)).findOne({ + model: "account", + where: [{ + field: "accountId", + value: accountId + }] + }); + }, + findAccountByProviderId: async (accountId, providerId) => { + return await (await getCurrentAdapter(adapter)).findOne({ + model: "account", + where: [{ + field: "accountId", + value: accountId + }, { + field: "providerId", + value: providerId + }] + }); + }, + findAccountByUserId: async (userId) => { + return await (await getCurrentAdapter(adapter)).findMany({ + model: "account", + where: [{ + field: "userId", + value: userId + }] + }); + }, + updateAccount: async (id, data) => { + return await updateWithHooks(data, [{ + field: "id", + value: id + }], "account", void 0); + }, + createVerificationValue: async (data) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...data + }, "verification", void 0); + }, + findVerificationValue: async (identifier) => { + const verification = await (await getCurrentAdapter(adapter)).findMany({ + model: "verification", + where: [{ + field: "identifier", + value: identifier + }], + sortBy: { + field: "createdAt", + direction: "desc" + }, + limit: 1 + }); + if (!options.verification?.disableCleanup) await deleteManyWithHooks([{ + field: "expiresAt", + value: /* @__PURE__ */ new Date(), + operator: "lt" + }], "verification", void 0); + return verification[0]; + }, + deleteVerificationValue: async (id) => { + await deleteWithHooks([{ + field: "id", + value: id + }], "verification", void 0); + }, + deleteVerificationByIdentifier: async (identifier) => { + await deleteWithHooks([{ + field: "identifier", + value: identifier + }], "verification", void 0); + }, + updateVerificationValue: async (id, data) => { + return await updateWithHooks(data, [{ + field: "id", + value: id + }], "verification", void 0); + } + }; +}; +function toZodSchema({ fields, isClientSide }) { + const zodFields = Object.keys(fields).reduce((acc, key) => { + const field = fields[key]; + if (!field) return acc; + if (isClientSide && field.input === false) return acc; + let schema2; + if (field.type === "json") schema2 = z.json ? z.json() : z.any(); + else if (field.type === "string[]" || field.type === "number[]") schema2 = z.array(field.type === "string[]" ? z.string() : z.number()); + else if (Array.isArray(field.type)) schema2 = z.any(); + else schema2 = z[field.type](); + if (field?.required === false) schema2 = schema2.optional(); + if (!isClientSide && field?.returned === false) return acc; + return { + ...acc, + [key]: schema2 + }; + }, {}); + return z.object(zodFields); +} +function getSchema(config2) { + const tables = (0, db_exports.getAuthTables)(config2); + const schema2 = {}; + for (const key in tables) { + const table = tables[key]; + const fields = table.fields; + const actualFields = {}; + Object.entries(fields).forEach(([key$1, field]) => { + actualFields[field.fieldName || key$1] = field; + if (field.references) { + const refTable = tables[field.references.model]; + if (refTable) actualFields[field.fieldName || key$1].references = { + ...field.references, + model: refTable.modelName, + field: field.references.field + }; + } + }); + if (schema2[table.modelName]) { + schema2[table.modelName].fields = { + ...schema2[table.modelName].fields, + ...actualFields + }; + continue; + } + schema2[table.modelName] = { + fields: actualFields, + order: table.order || Infinity + }; + } + return schema2; +} +function getKyselyDatabaseType(db2) { + if (!db2) return null; + if ("dialect" in db2) return getKyselyDatabaseType(db2.dialect); + if ("createDriver" in db2) { + if (db2 instanceof SqliteDialect) return "sqlite"; + if (db2 instanceof MysqlDialect) return "mysql"; + if (db2 instanceof PostgresDialect) return "postgres"; + if (db2 instanceof MssqlDialect) return "mssql"; + } + if ("aggregate" in db2) return "sqlite"; + if ("getConnection" in db2) return "mysql"; + if ("connect" in db2) return "postgres"; + if ("fileControl" in db2) return "sqlite"; + if ("open" in db2 && "close" in db2 && "prepare" in db2) return "sqlite"; + return null; +} +const createKyselyAdapter = async (config2) => { + const db2 = config2.database; + if (!db2) return { + kysely: null, + databaseType: null, + transaction: void 0 + }; + if ("db" in db2) return { + kysely: db2.db, + databaseType: db2.type, + transaction: db2.transaction + }; + if ("dialect" in db2) return { + kysely: new Kysely({ dialect: db2.dialect }), + databaseType: db2.type, + transaction: db2.transaction + }; + let dialect = void 0; + const databaseType = getKyselyDatabaseType(db2); + if ("createDriver" in db2) dialect = db2; + if ("aggregate" in db2 && !("createSession" in db2)) dialect = new SqliteDialect({ database: db2 }); + if ("getConnection" in db2) dialect = new MysqlDialect(db2); + if ("connect" in db2) dialect = new PostgresDialect({ pool: db2 }); + if ("fileControl" in db2) { + const { BunSqliteDialect } = await import("./bun-sqlite-dialect.js"); + dialect = new BunSqliteDialect({ database: db2 }); + } + if ("createSession" in db2) { + let DatabaseSync = void 0; + try { + const nodeSqlite = "node:sqlite"; + ({ DatabaseSync } = await import( + /* @vite-ignore */ + /* webpackIgnore: true */ + nodeSqlite + )); + } catch (error2) { + if (error2 !== null && typeof error2 === "object" && "code" in error2 && error2.code !== "ERR_UNKNOWN_BUILTIN_MODULE") throw error2; + } + if (DatabaseSync && db2 instanceof DatabaseSync) { + const { NodeSqliteDialect } = await import("./node-sqlite-dialect.js"); + dialect = new NodeSqliteDialect({ database: db2 }); + } + } + return { + kysely: dialect ? new Kysely({ dialect }) : null, + databaseType, + transaction: void 0 + }; +}; +const map = { + postgres: { + string: [ + "character varying", + "varchar", + "text", + "uuid" + ], + number: [ + "int4", + "integer", + "bigint", + "smallint", + "numeric", + "real", + "double precision" + ], + boolean: ["bool", "boolean"], + date: [ + "timestamptz", + "timestamp", + "date" + ], + json: ["json", "jsonb"] + }, + mysql: { + string: [ + "varchar", + "text", + "uuid" + ], + number: [ + "integer", + "int", + "bigint", + "smallint", + "decimal", + "float", + "double" + ], + boolean: ["boolean", "tinyint"], + date: [ + "timestamp", + "datetime", + "date" + ], + json: ["json"] + }, + sqlite: { + string: ["TEXT"], + number: ["INTEGER", "REAL"], + boolean: ["INTEGER", "BOOLEAN"], + date: ["DATE", "INTEGER"], + json: ["TEXT"] + }, + mssql: { + string: [ + "varchar", + "nvarchar", + "uniqueidentifier" + ], + number: [ + "int", + "bigint", + "smallint", + "decimal", + "float", + "double" + ], + boolean: ["bit", "smallint"], + date: [ + "datetime2", + "date", + "datetime" + ], + json: ["varchar", "nvarchar"] + } +}; +function matchType(columnDataType, fieldType, dbType) { + function normalize(type) { + return type.toLowerCase().split("(")[0].trim(); + } + if (fieldType === "string[]" || fieldType === "number[]") return columnDataType.toLowerCase().includes("json"); + const types2 = map[dbType]; + return (Array.isArray(fieldType) ? types2["string"].map((t) => t.toLowerCase()) : types2[fieldType].map((t) => t.toLowerCase())).includes(normalize(columnDataType)); +} +async function getPostgresSchema(db2) { + try { + const result = await sql`SHOW search_path`.execute(db2); + if (result.rows[0]?.search_path) return result.rows[0].search_path.split(",").map((s) => s.trim()).map((s) => s.replace(/^["']|["']$/g, "")).filter((s) => !s.startsWith("$"))[0] || "public"; + } catch { + } + return "public"; +} +async function getMigrations(config2) { + const betterAuthSchema = getSchema(config2); + const logger$1 = createLogger(config2.logger); + let { kysely: db2, databaseType: dbType } = await createKyselyAdapter(config2); + if (!dbType) { + logger$1.warn("Could not determine database type, defaulting to sqlite. Please provide a type in the database options to avoid this."); + dbType = "sqlite"; + } + if (!db2) { + logger$1.error("Only kysely adapter is supported for migrations. You can use `generate` command to generate the schema, if you're using a different adapter."); + process.exit(1); + } + let currentSchema = "public"; + if (dbType === "postgres") { + currentSchema = await getPostgresSchema(db2); + logger$1.debug(`PostgreSQL migration: Using schema '${currentSchema}' (from search_path)`); + try { + if (!(await sql` + SELECT schema_name + FROM information_schema.schemata + WHERE schema_name = ${currentSchema} + `.execute(db2)).rows[0]) logger$1.warn(`Schema '${currentSchema}' does not exist. Tables will be inspected from available schemas. Consider creating the schema first or checking your database configuration.`); + } catch (error2) { + logger$1.debug(`Could not verify schema existence: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + } + const allTableMetadata = await db2.introspection.getTables(); + let tableMetadata = allTableMetadata; + if (dbType === "postgres") try { + const tablesInSchema = await sql` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = ${currentSchema} + AND table_type = 'BASE TABLE' + `.execute(db2); + const tableNamesInSchema = new Set(tablesInSchema.rows.map((row) => row.table_name)); + tableMetadata = allTableMetadata.filter((table) => table.schema === currentSchema && tableNamesInSchema.has(table.name)); + logger$1.debug(`Found ${tableMetadata.length} table(s) in schema '${currentSchema}': ${tableMetadata.map((t) => t.name).join(", ") || "(none)"}`); + } catch (error2) { + logger$1.warn(`Could not filter tables by schema. Using all discovered tables. Error: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + const toBeCreated = []; + const toBeAdded = []; + for (const [key, value] of Object.entries(betterAuthSchema)) { + const table = tableMetadata.find((t) => t.name === key); + if (!table) { + const tIndex = toBeCreated.findIndex((t) => t.table === key); + const tableData = { + table: key, + fields: value.fields, + order: value.order || Infinity + }; + const insertIndex = toBeCreated.findIndex((t) => (t.order || Infinity) > tableData.order); + if (insertIndex === -1) if (tIndex === -1) toBeCreated.push(tableData); + else toBeCreated[tIndex].fields = { + ...toBeCreated[tIndex].fields, + ...value.fields + }; + else toBeCreated.splice(insertIndex, 0, tableData); + continue; + } + const toBeAddedFields = {}; + for (const [fieldName, field] of Object.entries(value.fields)) { + const column = table.columns.find((c) => c.name === fieldName); + if (!column) { + toBeAddedFields[fieldName] = field; + continue; + } + if (matchType(column.dataType, field.type, dbType)) continue; + else logger$1.warn(`Field ${fieldName} in table ${key} has a different type in the database. Expected ${field.type} but got ${column.dataType}.`); + } + if (Object.keys(toBeAddedFields).length > 0) toBeAdded.push({ + table: key, + fields: toBeAddedFields, + order: value.order || Infinity + }); + } + const migrations = []; + const useUUIDs = config2.advanced?.database?.generateId === "uuid"; + const useNumberId = config2.advanced?.database?.useNumberId || config2.advanced?.database?.generateId === "serial"; + function getType(field, fieldName) { + const type = field.type; + const provider = dbType || "sqlite"; + const typeMap = { + string: { + sqlite: "text", + postgres: "text", + mysql: field.unique ? "varchar(255)" : field.references ? "varchar(36)" : field.sortable ? "varchar(255)" : field.index ? "varchar(255)" : "text", + mssql: field.unique || field.sortable ? "varchar(255)" : field.references ? "varchar(36)" : "varchar(8000)" + }, + boolean: { + sqlite: "integer", + postgres: "boolean", + mysql: "boolean", + mssql: "smallint" + }, + number: { + sqlite: field.bigint ? "bigint" : "integer", + postgres: field.bigint ? "bigint" : "integer", + mysql: field.bigint ? "bigint" : "integer", + mssql: field.bigint ? "bigint" : "integer" + }, + date: { + sqlite: "date", + postgres: "timestamptz", + mysql: "timestamp(3)", + mssql: sql`datetime2(3)` + }, + json: { + sqlite: "text", + postgres: "jsonb", + mysql: "json", + mssql: "varchar(8000)" + }, + id: { + postgres: useNumberId ? sql`integer GENERATED BY DEFAULT AS IDENTITY` : useUUIDs ? "uuid" : "text", + mysql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + mssql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + sqlite: useNumberId ? "integer" : "text" + }, + foreignKeyId: { + postgres: useNumberId ? "integer" : useUUIDs ? "uuid" : "text", + mysql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + mssql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + sqlite: useNumberId ? "integer" : "text" + }, + "string[]": { + sqlite: "text", + postgres: "jsonb", + mysql: "json", + mssql: "varchar(8000)" + }, + "number[]": { + sqlite: "text", + postgres: "jsonb", + mysql: "json", + mssql: "varchar(8000)" + } + }; + if (fieldName === "id" || field.references?.field === "id") { + if (fieldName === "id") return typeMap.id[provider]; + return typeMap.foreignKeyId[provider]; + } + if (Array.isArray(type)) return "text"; + if (!(type in typeMap)) throw new Error(`Unsupported field type '${String(type)}' for field '${fieldName}'. Allowed types are: string, number, boolean, date, string[], number[]. If you need to store structured data, store it as a JSON string (type: "string") or split it into primitive fields. See https://better-auth.com/docs/advanced/schema#additional-fields`); + return typeMap[type][provider]; + } + const getModelName = initGetModelName({ + schema: getAuthTables(config2), + usePlural: false + }); + const getFieldName = initGetFieldName({ + schema: getAuthTables(config2), + usePlural: false + }); + function getReferencePath(model, field) { + try { + return `${getModelName(model)}.${getFieldName({ + model, + field + })}`; + } catch { + return `${model}.${field}`; + } + } + if (toBeAdded.length) for (const table of toBeAdded) for (const [fieldName, field] of Object.entries(table.fields)) { + const type = getType(field, fieldName); + const builder = db2.schema.alterTable(table.table); + if (field.index) { + const index = db2.schema.alterTable(table.table).addIndex(`${table.table}_${fieldName}_idx`); + migrations.push(index); + } + const built = builder.addColumn(fieldName, type, (col) => { + col = field.required !== false ? col.notNull() : col; + if (field.references) col = col.references(getReferencePath(field.references.model, field.references.field)).onDelete(field.references.onDelete || "cascade"); + if (field.unique) col = col.unique(); + if (field.type === "date" && typeof field.defaultValue === "function" && (dbType === "postgres" || dbType === "mysql" || dbType === "mssql")) if (dbType === "mysql") col = col.defaultTo(sql`CURRENT_TIMESTAMP(3)`); + else col = col.defaultTo(sql`CURRENT_TIMESTAMP`); + return col; + }); + migrations.push(built); + } + const toBeIndexed = []; + if (config2.advanced?.database?.useNumberId) logger$1.warn("`useNumberId` is deprecated. Please use `generateId` with `serial` instead."); + if (toBeCreated.length) for (const table of toBeCreated) { + const idType = getType({ type: useNumberId ? "number" : "string" }, "id"); + let dbT = db2.schema.createTable(table.table).addColumn("id", idType, (col) => { + if (useNumberId) { + if (dbType === "postgres") return col.primaryKey().notNull(); + else if (dbType === "sqlite") return col.primaryKey().notNull(); + else if (dbType === "mssql") return col.identity().primaryKey().notNull(); + return col.autoIncrement().primaryKey().notNull(); + } + if (useUUIDs) { + if (dbType === "postgres") return col.primaryKey().defaultTo(sql`pg_catalog.gen_random_uuid()`).notNull(); + return col.primaryKey().notNull(); + } + return col.primaryKey().notNull(); + }); + for (const [fieldName, field] of Object.entries(table.fields)) { + const type = getType(field, fieldName); + dbT = dbT.addColumn(fieldName, type, (col) => { + col = field.required !== false ? col.notNull() : col; + if (field.references) col = col.references(getReferencePath(field.references.model, field.references.field)).onDelete(field.references.onDelete || "cascade"); + if (field.unique) col = col.unique(); + if (field.type === "date" && typeof field.defaultValue === "function" && (dbType === "postgres" || dbType === "mysql" || dbType === "mssql")) if (dbType === "mysql") col = col.defaultTo(sql`CURRENT_TIMESTAMP(3)`); + else col = col.defaultTo(sql`CURRENT_TIMESTAMP`); + return col; + }); + if (field.index) { + const builder = db2.schema.createIndex(`${table.table}_${fieldName}_${field.unique ? "uidx" : "idx"}`).on(table.table).columns([fieldName]); + toBeIndexed.push(field.unique ? builder.unique() : builder); + } + } + migrations.push(dbT); + } + if (toBeIndexed.length) for (const index of toBeIndexed) migrations.push(index); + async function runMigrations() { + for (const migration of migrations) await migration.execute(); + } + async function compileMigrations() { + return migrations.map((m) => m.compile().sql).join(";\n\n") + ";"; + } + return { + toBeCreated, + toBeAdded, + runMigrations, + compileMigrations + }; +} +var db_exports = /* @__PURE__ */ __export({ + convertFromDB: () => convertFromDB, + convertToDB: () => convertToDB, + createFieldAttribute: () => createFieldAttribute, + createInternalAdapter: () => createInternalAdapter, + getAdapter: () => getAdapter, + getBaseAdapter: () => getBaseAdapter, + getMigrations: () => getMigrations, + getSchema: () => getSchema, + getWithHooks: () => getWithHooks, + matchType: () => matchType, + mergeSchema: () => mergeSchema, + parseAccountInput: () => parseAccountInput, + parseAccountOutput: () => parseAccountOutput, + parseAdditionalUserInput: () => parseAdditionalUserInput, + parseInputData: () => parseInputData, + parseSessionInput: () => parseSessionInput, + parseSessionOutput: () => parseSessionOutput, + parseUserInput: () => parseUserInput, + parseUserOutput: () => parseUserOutput, + toZodSchema: () => toZodSchema +}); +__reExport(db_exports, import___better_auth_core_db); +async function signJWT(payload, secret, expiresIn = 3600) { + return await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(Math.floor(Date.now() / 1e3) + expiresIn).sign(new TextEncoder().encode(secret)); +} +async function verifyJWT(token, secret) { + try { + return (await jwtVerify(token, new TextEncoder().encode(secret))).payload; + } catch { + return null; + } +} +const info = new Uint8Array([ + 66, + 101, + 116, + 116, + 101, + 114, + 65, + 117, + 116, + 104, + 46, + 106, + 115, + 32, + 71, + 101, + 110, + 101, + 114, + 97, + 116, + 101, + 100, + 32, + 69, + 110, + 99, + 114, + 121, + 112, + 116, + 105, + 111, + 110, + 32, + 75, + 101, + 121 +]); +const now = () => Date.now() / 1e3 | 0; +const alg = "dir"; +const enc = "A256CBC-HS512"; +async function symmetricEncodeJWT(payload, secret, salt, expiresIn = 3600) { + const encryptionSecret = hkdf(sha256, new TextEncoder().encode(secret), new TextEncoder().encode(salt), info, 64); + const thumbprint = await calculateJwkThumbprint({ + kty: "oct", + k: base64url.encode(encryptionSecret) + }, "sha256"); + return await new EncryptJWT(payload).setProtectedHeader({ + alg, + enc, + kid: thumbprint + }).setIssuedAt().setExpirationTime(now() + expiresIn).setJti(crypto.randomUUID()).encrypt(encryptionSecret); +} +async function symmetricDecodeJWT(token, secret, salt) { + if (!token) return null; + try { + const { payload } = await jwtDecrypt(token, async ({ kid }) => { + const encryptionSecret = hkdf(sha256, new TextEncoder().encode(secret), new TextEncoder().encode(salt), info, 64); + if (kid === void 0) return encryptionSecret; + if (kid === await calculateJwkThumbprint({ + kty: "oct", + k: base64url.encode(encryptionSecret) + }, "sha256")) return encryptionSecret; + throw new Error("no matching decryption secret"); + }, { + clockTolerance: 15, + keyManagementAlgorithms: [alg], + contentEncryptionAlgorithms: [enc, "A256GCM"] + }); + return payload; + } catch { + return null; + } +} +function constantTimeEqual(a, b) { + if (typeof a === "string") a = new TextEncoder().encode(a); + if (typeof b === "string") b = new TextEncoder().encode(b); + const aBuffer = new Uint8Array(a); + const bBuffer = new Uint8Array(b); + let c = aBuffer.length ^ bBuffer.length; + const length = Math.max(aBuffer.length, bBuffer.length); + for (let i = 0; i < length; i++) c |= (i < aBuffer.length ? aBuffer[i] : 0) ^ (i < bBuffer.length ? bBuffer[i] : 0); + return c === 0; +} +const config = { + N: 16384, + r: 16, + p: 1, + dkLen: 64 +}; +async function generateKey(password, salt) { + return await scryptAsync(password.normalize("NFKC"), salt, { + N: config.N, + p: config.p, + r: config.r, + dkLen: config.dkLen, + maxmem: 128 * config.N * config.r * 2 + }); +} +const hashPassword = async (password) => { + const salt = hex.encode(crypto.getRandomValues(new Uint8Array(16))); + const key = await generateKey(password, salt); + return `${salt}:${hex.encode(key)}`; +}; +const verifyPassword$1 = async ({ hash, password }) => { + const [salt, key] = hash.split(":"); + if (!salt || !key) throw new BetterAuthError("Invalid password hash"); + return constantTimeEqual(await generateKey(password, salt), hexToBytes(key)); +}; +const generateRandomString = createRandomStringGenerator("a-z", "0-9", "A-Z", "-_"); +const symmetricEncrypt = async ({ key, data }) => { + const keyAsBytes = await createHash("SHA-256").digest(key); + const dataAsBytes = utf8ToBytes(data); + return bytesToHex(managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes)).encrypt(dataAsBytes)); +}; +const symmetricDecrypt = async ({ key, data }) => { + const keyAsBytes = await createHash("SHA-256").digest(key); + const dataAsBytes = hexToBytes$1(data); + const chacha = managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes)); + return new TextDecoder().decode(chacha.decrypt(dataAsBytes)); +}; +const ALLOWED_COOKIE_SIZE = 4096; +const ESTIMATED_EMPTY_COOKIE_SIZE = 200; +const CHUNK_SIZE = ALLOWED_COOKIE_SIZE - ESTIMATED_EMPTY_COOKIE_SIZE; +function parseCookiesFromContext(ctx) { + const cookieHeader = ctx.headers?.get("cookie"); + if (!cookieHeader) return {}; + const cookies = {}; + const pairs = cookieHeader.split("; "); + for (const pair of pairs) { + const [name, ...valueParts] = pair.split("="); + if (name && valueParts.length > 0) cookies[name] = valueParts.join("="); + } + return cookies; +} +function getChunkIndex(cookieName) { + const parts = cookieName.split("."); + const lastPart = parts[parts.length - 1]; + const index = parseInt(lastPart || "0", 10); + return isNaN(index) ? 0 : index; +} +function readExistingChunks(cookieName, ctx) { + const chunks = {}; + const cookies = parseCookiesFromContext(ctx); + for (const [name, value] of Object.entries(cookies)) if (name.startsWith(cookieName)) chunks[name] = value; + return chunks; +} +function joinChunks(chunks) { + return Object.keys(chunks).sort((a, b) => { + return getChunkIndex(a) - getChunkIndex(b); + }).map((key) => chunks[key]).join(""); +} +function chunkCookie(storeName, cookie, chunks, logger2) { + const chunkCount = Math.ceil(cookie.value.length / CHUNK_SIZE); + if (chunkCount === 1) { + chunks[cookie.name] = cookie.value; + return [cookie]; + } + const cookies = []; + for (let i = 0; i < chunkCount; i++) { + const name = `${cookie.name}.${i}`; + const start = i * CHUNK_SIZE; + const value = cookie.value.substring(start, start + CHUNK_SIZE); + cookies.push({ + ...cookie, + name, + value + }); + chunks[name] = value; + } + logger2.debug(`CHUNKING_${storeName.toUpperCase()}_COOKIE`, { + message: `${storeName} cookie exceeds allowed ${ALLOWED_COOKIE_SIZE} bytes.`, + emptyCookieSize: ESTIMATED_EMPTY_COOKIE_SIZE, + valueSize: cookie.value.length, + chunkCount, + chunks: cookies.map((c) => c.value.length + ESTIMATED_EMPTY_COOKIE_SIZE) + }); + return cookies; +} +function getCleanCookies(chunks, cookieOptions) { + const cleanedChunks = {}; + for (const name in chunks) cleanedChunks[name] = { + name, + value: "", + attributes: { + ...cookieOptions, + maxAge: 0 + } + }; + return cleanedChunks; +} +const storeFactory = (storeName) => (cookieName, cookieOptions, ctx) => { + const chunks = readExistingChunks(cookieName, ctx); + const logger2 = ctx.context.logger; + return { + getValue() { + return joinChunks(chunks); + }, + hasChunks() { + return Object.keys(chunks).length > 0; + }, + chunk(value, options) { + const cleanedChunks = getCleanCookies(chunks, cookieOptions); + for (const name in chunks) delete chunks[name]; + const cookies = cleanedChunks; + const chunked = chunkCookie(storeName, { + name: cookieName, + value, + attributes: { + ...cookieOptions, + ...options + } + }, chunks, logger2); + for (const chunk of chunked) cookies[chunk.name] = chunk; + return Object.values(cookies); + }, + clean() { + const cleanedChunks = getCleanCookies(chunks, cookieOptions); + for (const name in chunks) delete chunks[name]; + return Object.values(cleanedChunks); + }, + setCookies(cookies) { + for (const cookie of cookies) ctx.setCookie(cookie.name, cookie.value, cookie.attributes); + } + }; +}; +const createSessionStore = storeFactory("Session"); +const createAccountStore = storeFactory("Account"); +function getChunkedCookie(ctx, cookieName) { + const value = ctx.getCookie(cookieName); + if (value) return value; + const chunks = []; + const cookieHeader = ctx.headers?.get("cookie"); + if (!cookieHeader) return null; + const cookies = {}; + const pairs = cookieHeader.split("; "); + for (const pair of pairs) { + const [name, ...valueParts] = pair.split("="); + if (name && valueParts.length > 0) cookies[name] = valueParts.join("="); + } + for (const [name, val] of Object.entries(cookies)) if (name.startsWith(cookieName + ".")) { + const indexStr = name.split(".").at(-1); + const index = parseInt(indexStr || "0", 10); + if (!isNaN(index)) chunks.push({ + index, + value: val + }); + } + if (chunks.length > 0) { + chunks.sort((a, b) => a.index - b.index); + return chunks.map((c) => c.value).join(""); + } + return null; +} +async function setAccountCookie(c, accountData) { + const accountDataCookie = c.context.authCookies.accountData; + const options = { + maxAge: 300, + ...accountDataCookie.attributes + }; + const data = await symmetricEncodeJWT(accountData, c.context.secret, "better-auth-account", options.maxAge); + if (data.length > ALLOWED_COOKIE_SIZE) { + const accountStore = createAccountStore(accountDataCookie.name, options, c); + const cookies = accountStore.chunk(data, options); + accountStore.setCookies(cookies); + } else { + const accountStore = createAccountStore(accountDataCookie.name, options, c); + if (accountStore.hasChunks()) { + const cleanCookies = accountStore.clean(); + accountStore.setCookies(cleanCookies); + } + c.setCookie(accountDataCookie.name, data, options); + } +} +async function getAccountCookie(c) { + const accountCookie = getChunkedCookie(c, c.context.authCookies.accountData.name); + if (accountCookie) { + const accountData = safeJSONParse(await symmetricDecodeJWT(accountCookie, c.context.secret, "better-auth-account")); + if (accountData) return accountData; + } + return null; +} +const getSessionQuerySchema = z.optional(z.object({ + disableCookieCache: z.coerce.boolean().meta({ description: "Disable cookie cache and fetch session from database" }).optional(), + disableRefresh: z.coerce.boolean().meta({ description: "Disable session refresh. Useful for checking session status, without updating the session" }).optional() +})); +function isPromise(obj) { + return !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function"; +} +const SEC = 1e3; +const MIN = SEC * 60; +const HOUR = MIN * 60; +const DAY = HOUR * 24; +const WEEK = DAY * 7; +const MONTH = DAY * 30; +const YEAR = DAY * 365.25; +const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)(?: (ago|from now))?$/i; +function parse(value) { + const match = REGEX.exec(value); + if (!match || match[4] && match[1]) throw new TypeError(`Invalid time string format: "${value}". Use formats like "7d", "30m", "1 hour", etc.`); + const n = parseFloat(match[2]); + const unit = match[3].toLowerCase(); + let result; + switch (unit) { + case "years": + case "year": + case "yrs": + case "yr": + case "y": + result = n * YEAR; + break; + case "months": + case "month": + case "mo": + result = n * MONTH; + break; + case "weeks": + case "week": + case "w": + result = n * WEEK; + break; + case "days": + case "day": + case "d": + result = n * DAY; + break; + case "hours": + case "hour": + case "hrs": + case "hr": + case "h": + result = n * HOUR; + break; + case "minutes": + case "minute": + case "mins": + case "min": + case "m": + result = n * MIN; + break; + case "seconds": + case "second": + case "secs": + case "sec": + case "s": + result = n * SEC; + break; + default: + throw new TypeError(`Unknown time unit: "${unit}"`); + } + if (match[1] === "-" || match[4] === "ago") return -result; + return result; +} +function ms(value) { + return parse(value); +} +function sec(value) { + return Math.round(parse(value) / 1e3); +} +const SECURE_COOKIE_PREFIX = "__Secure-"; +function splitSetCookieHeader(setCookie) { + if (!setCookie) return []; + const result = []; + let current = ""; + let i = 0; + while (i < setCookie.length) { + const c = setCookie[i]; + if (c === ",") { + const lower = current.toLowerCase(); + if (lower.includes("expires=") && !lower.includes("gmt")) { + current += c; + i++; + } else { + const trimmed$1 = current.trim(); + if (trimmed$1) result.push(trimmed$1); + current = ""; + i++; + if (i < setCookie.length && setCookie[i] === " ") i++; + } + continue; + } + current += c; + i++; + } + const trimmed = current.trim(); + if (trimmed) result.push(trimmed); + return result; +} +function parseSetCookieHeader(setCookie) { + const cookies = /* @__PURE__ */ new Map(); + splitSetCookieHeader(setCookie).forEach((cookieString) => { + const [nameValue, ...attributes] = cookieString.split(";").map((part) => part.trim()); + const [name, ...valueParts] = (nameValue || "").split("="); + const value = valueParts.join("="); + if (!name || value === void 0) return; + const attrObj = { value }; + attributes.forEach((attribute) => { + const [attrName, ...attrValueParts] = attribute.split("="); + const attrValue = attrValueParts.join("="); + const normalizedAttrName = attrName.trim().toLowerCase(); + switch (normalizedAttrName) { + case "max-age": + attrObj["max-age"] = attrValue ? parseInt(attrValue.trim(), 10) : void 0; + break; + case "expires": + attrObj.expires = attrValue ? new Date(attrValue.trim()) : void 0; + break; + case "domain": + attrObj.domain = attrValue ? attrValue.trim() : void 0; + break; + case "path": + attrObj.path = attrValue ? attrValue.trim() : void 0; + break; + case "secure": + attrObj.secure = true; + break; + case "httponly": + attrObj.httponly = true; + break; + case "samesite": + attrObj.samesite = attrValue ? attrValue.trim().toLowerCase() : void 0; + break; + default: + attrObj[normalizedAttrName] = attrValue ? attrValue.trim() : true; + break; + } + }); + cookies.set(name, attrObj); + }); + return cookies; +} +function createCookieGetter(options) { + const secureCookiePrefix = (options.advanced?.useSecureCookies !== void 0 ? options.advanced?.useSecureCookies : options.baseURL ? options.baseURL.startsWith("https://") ? true : false : isProduction) ? SECURE_COOKIE_PREFIX : ""; + const crossSubdomainEnabled = !!options.advanced?.crossSubDomainCookies?.enabled; + const domain = crossSubdomainEnabled ? options.advanced?.crossSubDomainCookies?.domain || (options.baseURL ? new URL(options.baseURL).hostname : void 0) : void 0; + if (crossSubdomainEnabled && !domain) throw new BetterAuthError("baseURL is required when crossSubdomainCookies are enabled"); + function createCookie(cookieName, overrideAttributes = {}) { + const prefix = options.advanced?.cookiePrefix || "better-auth"; + const name = options.advanced?.cookies?.[cookieName]?.name || `${prefix}.${cookieName}`; + const attributes = options.advanced?.cookies?.[cookieName]?.attributes; + return { + name: `${secureCookiePrefix}${name}`, + attributes: { + secure: !!secureCookiePrefix, + sameSite: "lax", + path: "/", + httpOnly: true, + ...crossSubdomainEnabled ? { domain } : {}, + ...options.advanced?.defaultCookieAttributes, + ...overrideAttributes, + ...attributes + } + }; + } + return createCookie; +} +function getCookies(options) { + const createCookie = createCookieGetter(options); + const sessionToken = createCookie("session_token", { maxAge: options.session?.expiresIn || sec("7d") }); + const sessionData = createCookie("session_data", { maxAge: options.session?.cookieCache?.maxAge || 300 }); + const accountData = createCookie("account_data", { maxAge: options.session?.cookieCache?.maxAge || 300 }); + const dontRememberToken = createCookie("dont_remember"); + return { + sessionToken: { + name: sessionToken.name, + attributes: sessionToken.attributes + }, + sessionData: { + name: sessionData.name, + attributes: sessionData.attributes + }, + dontRememberToken: { + name: dontRememberToken.name, + attributes: dontRememberToken.attributes + }, + accountData: { + name: accountData.name, + attributes: accountData.attributes + } + }; +} +async function setCookieCache(ctx, session, dontRememberMe) { + if (!ctx.context.options.session?.cookieCache?.enabled) return; + const filteredSession = filterOutputFields(session.session, ctx.context.options.session?.additionalFields); + const filteredUser = parseUserOutput(ctx.context.options, session.user); + const versionConfig = ctx.context.options.session?.cookieCache?.version; + let version = "1"; + if (versionConfig) { + if (typeof versionConfig === "string") version = versionConfig; + else if (typeof versionConfig === "function") { + const result = versionConfig(session.session, session.user); + version = isPromise(result) ? await result : result; + } + } + const sessionData = { + session: filteredSession, + user: filteredUser, + updatedAt: Date.now(), + version + }; + const options = { + ...ctx.context.authCookies.sessionData.attributes, + maxAge: dontRememberMe ? void 0 : ctx.context.authCookies.sessionData.attributes.maxAge + }; + const expiresAtDate = getDate(options.maxAge || 60, "sec").getTime(); + const strategy = ctx.context.options.session?.cookieCache?.strategy || "compact"; + let data; + if (strategy === "jwe") data = await symmetricEncodeJWT(sessionData, ctx.context.secret, "better-auth-session", options.maxAge || 300); + else if (strategy === "jwt") data = await signJWT(sessionData, ctx.context.secret, options.maxAge || 300); + else data = base64Url.encode(JSON.stringify({ + session: sessionData, + expiresAt: expiresAtDate, + signature: await createHMAC("SHA-256", "base64urlnopad").sign(ctx.context.secret, JSON.stringify({ + ...sessionData, + expiresAt: expiresAtDate + })) + }), { padding: false }); + if (data.length > 4093) { + const sessionStore = createSessionStore(ctx.context.authCookies.sessionData.name, options, ctx); + const cookies = sessionStore.chunk(data, options); + sessionStore.setCookies(cookies); + } else { + const sessionStore = createSessionStore(ctx.context.authCookies.sessionData.name, options, ctx); + if (sessionStore.hasChunks()) { + const cleanCookies = sessionStore.clean(); + sessionStore.setCookies(cleanCookies); + } + ctx.setCookie(ctx.context.authCookies.sessionData.name, data, options); + } + if (ctx.context.options.account?.storeAccountCookie) { + const accountData = await getAccountCookie(ctx); + if (accountData) await setAccountCookie(ctx, accountData); + } +} +async function setSessionCookie(ctx, session, dontRememberMe, overrides) { + const dontRememberMeCookie = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret); + dontRememberMe = dontRememberMe !== void 0 ? dontRememberMe : !!dontRememberMeCookie; + const options = ctx.context.authCookies.sessionToken.attributes; + const maxAge = dontRememberMe ? void 0 : ctx.context.sessionConfig.expiresIn; + await ctx.setSignedCookie(ctx.context.authCookies.sessionToken.name, session.session.token, ctx.context.secret, { + ...options, + maxAge, + ...overrides + }); + if (dontRememberMe) await ctx.setSignedCookie(ctx.context.authCookies.dontRememberToken.name, "true", ctx.context.secret, ctx.context.authCookies.dontRememberToken.attributes); + await setCookieCache(ctx, session, dontRememberMe); + ctx.context.setNewSession(session); +} +function expireCookie(ctx, cookie) { + ctx.setCookie(cookie.name, "", { + ...cookie.attributes, + maxAge: 0 + }); +} +function deleteSessionCookie(ctx, skipDontRememberMe) { + expireCookie(ctx, ctx.context.authCookies.sessionToken); + expireCookie(ctx, ctx.context.authCookies.sessionData); + if (ctx.context.options.account?.storeAccountCookie) { + expireCookie(ctx, ctx.context.authCookies.accountData); + const accountStore = createAccountStore(ctx.context.authCookies.accountData.name, ctx.context.authCookies.accountData.attributes, ctx); + const cleanCookies$1 = accountStore.clean(); + accountStore.setCookies(cleanCookies$1); + } + if (ctx.context.oauthConfig.storeStateStrategy === "cookie") expireCookie(ctx, ctx.context.createAuthCookie("oauth_state")); + const sessionStore = createSessionStore(ctx.context.authCookies.sessionData.name, ctx.context.authCookies.sessionData.attributes, ctx); + const cleanCookies = sessionStore.clean(); + sessionStore.setCookies(cleanCookies); + expireCookie(ctx, ctx.context.authCookies.dontRememberToken); +} +const getSession = () => createAuthEndpoint("/get-session", { + method: "GET", + operationId: "getSession", + query: getSessionQuerySchema, + requireHeaders: true, + metadata: { openapi: { + operationId: "getSession", + description: "Get the current session", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + nullable: true, + properties: { + session: { $ref: "#/components/schemas/Session" }, + user: { $ref: "#/components/schemas/User" } + }, + required: ["session", "user"] + } } } + } } + } } +}, async (ctx) => { + try { + const sessionCookieToken = await ctx.getSignedCookie(ctx.context.authCookies.sessionToken.name, ctx.context.secret); + if (!sessionCookieToken) return null; + const sessionDataCookie = getChunkedCookie(ctx, ctx.context.authCookies.sessionData.name); + let sessionDataPayload = null; + if (sessionDataCookie) { + const strategy = ctx.context.options.session?.cookieCache?.strategy || "compact"; + if (strategy === "jwe") { + const payload = await symmetricDecodeJWT(sessionDataCookie, ctx.context.secret, "better-auth-session"); + if (payload && payload.session && payload.user) sessionDataPayload = { + session: { + session: payload.session, + user: payload.user, + updatedAt: payload.updatedAt, + version: payload.version + }, + expiresAt: payload.exp ? payload.exp * 1e3 : Date.now() + }; + else { + expireCookie(ctx, ctx.context.authCookies.sessionData); + return ctx.json(null); + } + } else if (strategy === "jwt") { + const payload = await verifyJWT(sessionDataCookie, ctx.context.secret); + if (payload && payload.session && payload.user) sessionDataPayload = { + session: { + session: payload.session, + user: payload.user, + updatedAt: payload.updatedAt, + version: payload.version + }, + expiresAt: payload.exp ? payload.exp * 1e3 : Date.now() + }; + else { + expireCookie(ctx, ctx.context.authCookies.sessionData); + return ctx.json(null); + } + } else { + const parsed = safeJSONParse(binary.decode(base64Url.decode(sessionDataCookie))); + if (parsed) if (await createHMAC("SHA-256", "base64urlnopad").verify(ctx.context.secret, JSON.stringify({ + ...parsed.session, + expiresAt: parsed.expiresAt + }), parsed.signature)) sessionDataPayload = parsed; + else { + expireCookie(ctx, ctx.context.authCookies.sessionData); + return ctx.json(null); + } + } + } + const dontRememberMe = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret); + if (sessionDataPayload?.session && ctx.context.options.session?.cookieCache?.enabled && !ctx.query?.disableCookieCache) { + const session$1 = sessionDataPayload.session; + const versionConfig = ctx.context.options.session?.cookieCache?.version; + let expectedVersion = "1"; + if (versionConfig) { + if (typeof versionConfig === "string") expectedVersion = versionConfig; + else if (typeof versionConfig === "function") { + const result = versionConfig(session$1.session, session$1.user); + expectedVersion = result instanceof Promise ? await result : result; + } + } + if ((session$1.version || "1") !== expectedVersion) expireCookie(ctx, ctx.context.authCookies.sessionData); + else { + const cachedSessionExpiresAt = new Date(session$1.session.expiresAt); + if (sessionDataPayload.expiresAt < Date.now() || cachedSessionExpiresAt < /* @__PURE__ */ new Date()) expireCookie(ctx, ctx.context.authCookies.sessionData); + else { + const cookieRefreshCache = ctx.context.sessionConfig.cookieRefreshCache; + if (cookieRefreshCache === false) { + ctx.context.session = session$1; + const parsedSession$2 = parseSessionOutput(ctx.context.options, { + ...session$1.session, + expiresAt: new Date(session$1.session.expiresAt), + createdAt: new Date(session$1.session.createdAt), + updatedAt: new Date(session$1.session.updatedAt) + }); + const parsedUser$2 = parseUserOutput(ctx.context.options, { + ...session$1.user, + createdAt: new Date(session$1.user.createdAt), + updatedAt: new Date(session$1.user.updatedAt) + }); + return ctx.json({ + session: parsedSession$2, + user: parsedUser$2 + }); + } + if (sessionDataPayload.expiresAt - Date.now() < cookieRefreshCache.updateAge * 1e3) { + const newExpiresAt = getDate(ctx.context.options.session?.cookieCache?.maxAge || 300, "sec"); + const refreshedSession = { + session: { + ...session$1.session, + expiresAt: newExpiresAt + }, + user: session$1.user, + updatedAt: Date.now() + }; + await setCookieCache(ctx, refreshedSession, false); + const parsedRefreshedSession = parseSessionOutput(ctx.context.options, { + ...refreshedSession.session, + expiresAt: new Date(refreshedSession.session.expiresAt), + createdAt: new Date(refreshedSession.session.createdAt), + updatedAt: new Date(refreshedSession.session.updatedAt) + }); + const parsedRefreshedUser = parseUserOutput(ctx.context.options, { + ...refreshedSession.user, + createdAt: new Date(refreshedSession.user.createdAt), + updatedAt: new Date(refreshedSession.user.updatedAt) + }); + ctx.context.session = { + session: parsedRefreshedSession, + user: parsedRefreshedUser + }; + return ctx.json({ + session: parsedRefreshedSession, + user: parsedRefreshedUser + }); + } + const parsedSession$1 = parseSessionOutput(ctx.context.options, { + ...session$1.session, + expiresAt: new Date(session$1.session.expiresAt), + createdAt: new Date(session$1.session.createdAt), + updatedAt: new Date(session$1.session.updatedAt) + }); + const parsedUser$1 = parseUserOutput(ctx.context.options, { + ...session$1.user, + createdAt: new Date(session$1.user.createdAt), + updatedAt: new Date(session$1.user.updatedAt) + }); + ctx.context.session = { + session: parsedSession$1, + user: parsedUser$1 + }; + return ctx.json({ + session: parsedSession$1, + user: parsedUser$1 + }); + } + } + } + const session = await ctx.context.internalAdapter.findSession(sessionCookieToken); + ctx.context.session = session; + if (!session || session.session.expiresAt < /* @__PURE__ */ new Date()) { + deleteSessionCookie(ctx); + if (session) + await ctx.context.internalAdapter.deleteSession(session.session.token); + return ctx.json(null); + } + if (dontRememberMe || ctx.query?.disableRefresh) { + const parsedSession$1 = parseSessionOutput(ctx.context.options, session.session); + const parsedUser$1 = parseUserOutput(ctx.context.options, session.user); + return ctx.json({ + session: parsedSession$1, + user: parsedUser$1 + }); + } + const expiresIn = ctx.context.sessionConfig.expiresIn; + const updateAge = ctx.context.sessionConfig.updateAge; + if (session.session.expiresAt.valueOf() - expiresIn * 1e3 + updateAge * 1e3 <= Date.now() && (!ctx.query?.disableRefresh || !ctx.context.options.session?.disableSessionRefresh)) { + const updatedSession = await ctx.context.internalAdapter.updateSession(session.session.token, { + expiresAt: getDate(ctx.context.sessionConfig.expiresIn, "sec"), + updatedAt: /* @__PURE__ */ new Date() + }); + if (!updatedSession) { + deleteSessionCookie(ctx); + return ctx.json(null, { status: 401 }); + } + const maxAge = (updatedSession.expiresAt.valueOf() - Date.now()) / 1e3; + await setSessionCookie(ctx, { + session: updatedSession, + user: session.user + }, false, { maxAge }); + const parsedUpdatedSession = parseSessionOutput(ctx.context.options, updatedSession); + const parsedUser$1 = parseUserOutput(ctx.context.options, session.user); + return ctx.json({ + session: parsedUpdatedSession, + user: parsedUser$1 + }); + } + await setCookieCache(ctx, session, !!dontRememberMe); + const parsedSession = parseSessionOutput(ctx.context.options, session.session); + const parsedUser = parseUserOutput(ctx.context.options, session.user); + return ctx.json({ + session: parsedSession, + user: parsedUser + }); + } catch (error2) { + ctx.context.logger.error("INTERNAL_SERVER_ERROR", error2); + throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_GET_SESSION }); + } +}); +const getSessionFromCtx = async (ctx, config2) => { + if (ctx.context.session) return ctx.context.session; + const session = await getSession()({ + ...ctx, + asResponse: false, + headers: ctx.headers, + returnHeaders: false, + returnStatus: false, + query: { + ...config2, + ...ctx.query + } + }).catch((e) => { + return null; + }); + ctx.context.session = session; + return session; +}; +const sessionMiddleware = createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session?.session) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +const sensitiveSessionMiddleware = createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx, { disableCookieCache: true }); + if (!session?.session) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session?.session && (ctx.request || ctx.headers)) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +const freshSessionMiddleware = createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session?.session) throw new APIError("UNAUTHORIZED"); + if (ctx.context.sessionConfig.freshAge === 0) return { session }; + const freshAge = ctx.context.sessionConfig.freshAge; + const lastUpdated = new Date(session.session.updatedAt || session.session.createdAt).getTime(); + if (!(Date.now() - lastUpdated < freshAge * 1e3)) throw new APIError("FORBIDDEN", { message: "Session is not fresh" }); + return { session }; +}); +const listSessions = () => createAuthEndpoint("/list-sessions", { + method: "GET", + operationId: "listUserSessions", + use: [sessionMiddleware], + requireHeaders: true, + metadata: { openapi: { + operationId: "listUserSessions", + description: "List all active sessions for the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "array", + items: { $ref: "#/components/schemas/Session" } + } } } + } } + } } +}, async (ctx) => { + try { + const activeSessions = (await ctx.context.internalAdapter.listSessions(ctx.context.session.user.id)).filter((session) => { + return session.expiresAt > /* @__PURE__ */ new Date(); + }); + return ctx.json(activeSessions.map((session) => parseSessionOutput(ctx.context.options, session))); + } catch (e) { + ctx.context.logger.error(e); + throw ctx.error("INTERNAL_SERVER_ERROR"); + } +}); +const revokeSession = createAuthEndpoint("/revoke-session", { + method: "POST", + body: z.object({ token: z.string().meta({ description: "The token to revoke" }) }), + use: [sensitiveSessionMiddleware], + requireHeaders: true, + metadata: { openapi: { + description: "Revoke a single session", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { token: { + type: "string", + description: "The token to revoke" + } }, + required: ["token"] + } } } }, + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if the session was revoked successfully" + } }, + required: ["status"] + } } } + } } + } } +}, async (ctx) => { + const token = ctx.body.token; + if ((await ctx.context.internalAdapter.findSession(token))?.session.userId === ctx.context.session.user.id) try { + await ctx.context.internalAdapter.deleteSession(token); + } catch (error2) { + ctx.context.logger.error(error2 && typeof error2 === "object" && "name" in error2 ? error2.name : "", error2); + throw new APIError("INTERNAL_SERVER_ERROR"); + } + return ctx.json({ status: true }); +}); +const revokeSessions = createAuthEndpoint("/revoke-sessions", { + method: "POST", + use: [sensitiveSessionMiddleware], + requireHeaders: true, + metadata: { openapi: { + description: "Revoke all sessions for the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if all sessions were revoked successfully" + } }, + required: ["status"] + } } } + } } + } } +}, async (ctx) => { + try { + await ctx.context.internalAdapter.deleteSessions(ctx.context.session.user.id); + } catch (error2) { + ctx.context.logger.error(error2 && typeof error2 === "object" && "name" in error2 ? error2.name : "", error2); + throw new APIError("INTERNAL_SERVER_ERROR"); + } + return ctx.json({ status: true }); +}); +const revokeOtherSessions = createAuthEndpoint("/revoke-other-sessions", { + method: "POST", + requireHeaders: true, + use: [sensitiveSessionMiddleware], + metadata: { openapi: { + description: "Revoke all other sessions for the user except the current one", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if all other sessions were revoked successfully" + } }, + required: ["status"] + } } } + } } + } } +}, async (ctx) => { + const session = ctx.context.session; + if (!session.user) throw new APIError("UNAUTHORIZED"); + const otherSessions = (await ctx.context.internalAdapter.listSessions(session.user.id)).filter((session$1) => { + return session$1.expiresAt > /* @__PURE__ */ new Date(); + }).filter((session$1) => session$1.token !== ctx.context.session.session.token); + await Promise.all(otherSessions.map((session$1) => ctx.context.internalAdapter.deleteSession(session$1.token))); + return ctx.json({ status: true }); +}); +const stateDataSchema = z.looseObject({ + callbackURL: z.string(), + codeVerifier: z.string(), + errorURL: z.string().optional(), + newUserURL: z.string().optional(), + expiresAt: z.number(), + link: z.object({ + email: z.string(), + userId: z.coerce.string() + }).optional(), + requestSignUp: z.boolean().optional() +}); +var StateError = class extends BetterAuthError { + code; + details; + constructor(message, options) { + super(message, options); + this.code = options.code; + this.details = options.details; + } +}; +async function generateGenericState(c, stateData, settings) { + const state = generateRandomString(32); + if (c.context.oauthConfig.storeStateStrategy === "cookie") { + const encryptedData = await symmetricEncrypt({ + key: c.context.secret, + data: JSON.stringify(stateData) + }); + const stateCookie$1 = c.context.createAuthCookie("oauth_state", { maxAge: 600 }); + c.setCookie(stateCookie$1.name, encryptedData, stateCookie$1.attributes); + return { + state, + codeVerifier: stateData.codeVerifier + }; + } + const stateCookie = c.context.createAuthCookie("state", { maxAge: 300 }); + await c.setSignedCookie(stateCookie.name, state, c.context.secret, stateCookie.attributes); + const expiresAt = /* @__PURE__ */ new Date(); + expiresAt.setMinutes(expiresAt.getMinutes() + 10); + const verification = await c.context.internalAdapter.createVerificationValue({ + value: JSON.stringify(stateData), + identifier: state, + expiresAt + }); + if (!verification) throw new StateError("Unable to create verification. Make sure the database adapter is properly working and there is a verification table in the database", { code: "state_generation_error" }); + return { + state: verification.identifier, + codeVerifier: stateData.codeVerifier + }; +} +async function parseGenericState(c, state, settings) { + const storeStateStrategy = c.context.oauthConfig.storeStateStrategy; + let parsedData; + if (storeStateStrategy === "cookie") { + const stateCookie = c.context.createAuthCookie("oauth_state"); + const encryptedData = c.getCookie(stateCookie.name); + if (!encryptedData) throw new StateError("State mismatch: auth state cookie not found", { + code: "state_mismatch", + details: { state } + }); + try { + const decryptedData = await symmetricDecrypt({ + key: c.context.secret, + data: encryptedData + }); + parsedData = stateDataSchema.parse(JSON.parse(decryptedData)); + } catch (error2) { + throw new StateError("State invalid: Failed to decrypt or parse auth state", { + code: "state_invalid", + details: { state }, + cause: error2 + }); + } + expireCookie(c, stateCookie); + } else { + const data = await c.context.internalAdapter.findVerificationValue(state); + if (!data) throw new StateError("State mismatch: verification not found", { + code: "state_mismatch", + details: { state } + }); + parsedData = stateDataSchema.parse(JSON.parse(data.value)); + const stateCookie = c.context.createAuthCookie("state"); + const stateCookieValue = await c.getSignedCookie(stateCookie.name, c.context.secret); + if (!c.context.oauthConfig.skipStateCookieCheck && (!stateCookieValue || stateCookieValue !== state)) throw new StateError("State mismatch: State not persisted correctly", { + code: "state_security_mismatch", + details: { state } + }); + expireCookie(c, stateCookie); + await c.context.internalAdapter.deleteVerificationValue(data.id); + } + if (parsedData.expiresAt < Date.now()) throw new StateError("Invalid state: request expired", { + code: "state_mismatch", + details: { expiresAt: parsedData.expiresAt } + }); + return parsedData; +} +async function generateState(c, link, additionalData) { + const callbackURL = c.body?.callbackURL || c.context.options.baseURL; + if (!callbackURL) throw new APIError("BAD_REQUEST", { message: "callbackURL is required" }); + const codeVerifier = generateRandomString(128); + const stateData = { + ...additionalData ? additionalData : {}, + callbackURL, + codeVerifier, + errorURL: c.body?.errorCallbackURL, + newUserURL: c.body?.newUserCallbackURL, + link, + expiresAt: Date.now() + 600 * 1e3, + requestSignUp: c.body?.requestSignUp + }; + await setOAuthState(stateData); + try { + return generateGenericState(c, stateData); + } catch (error2) { + c.context.logger.error("Failed to create verification", error2); + throw new APIError("INTERNAL_SERVER_ERROR", { + message: "Unable to create verification", + cause: error2 + }); + } +} +async function parseState(c) { + const state = c.query.state || c.body.state; + const errorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`; + let parsedData; + try { + parsedData = await parseGenericState(c, state); + } catch (error2) { + c.context.logger.error("Failed to parse state", error2); + if (error2 instanceof StateError && error2.code === "state_security_mismatch") throw c.redirect(`${errorURL}?error=state_mismatch`); + throw c.redirect(`${errorURL}?error=please_restart_the_process`); + } + if (!parsedData.errorURL) parsedData.errorURL = errorURL; + if (parsedData) await setOAuthState(parsedData); + return parsedData; +} +function decryptOAuthToken(token, ctx) { + if (!token) return token; + if (ctx.options.account?.encryptOAuthTokens) return symmetricDecrypt({ + key: ctx.secret, + data: token + }); + return token; +} +function setTokenUtil(token, ctx) { + if (ctx.options.account?.encryptOAuthTokens && token) return symmetricEncrypt({ + key: ctx.secret, + data: token + }); + return token; +} +const listUserAccounts = createAuthEndpoint("/list-accounts", { + method: "GET", + use: [sessionMiddleware], + metadata: { openapi: { + operationId: "listUserAccounts", + description: "List all accounts linked to the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "array", + items: { + type: "object", + properties: { + id: { type: "string" }, + providerId: { type: "string" }, + createdAt: { + type: "string", + format: "date-time" + }, + updatedAt: { + type: "string", + format: "date-time" + }, + accountId: { type: "string" }, + userId: { type: "string" }, + scopes: { + type: "array", + items: { type: "string" } + } + }, + required: [ + "id", + "providerId", + "createdAt", + "updatedAt", + "accountId", + "userId", + "scopes" + ] + } + } } } + } } + } } +}, async (c) => { + const session = c.context.session; + const accounts = await c.context.internalAdapter.findAccounts(session.user.id); + return c.json(accounts.map((a) => { + const { scope, ...parsed } = parseAccountOutput(c.context.options, a); + return { + ...parsed, + scopes: scope?.split(",") || [] + }; + })); +}); +const linkSocialAccount = createAuthEndpoint("/link-social", { + method: "POST", + requireHeaders: true, + body: z.object({ + callbackURL: z.string().meta({ description: "The URL to redirect to after the user has signed in" }).optional(), + provider: SocialProviderListEnum, + idToken: z.object({ + token: z.string(), + nonce: z.string().optional(), + accessToken: z.string().optional(), + refreshToken: z.string().optional(), + scopes: z.array(z.string()).optional() + }).optional(), + requestSignUp: z.boolean().optional(), + scopes: z.array(z.string()).meta({ description: "Additional scopes to request from the provider" }).optional(), + errorCallbackURL: z.string().meta({ description: "The URL to redirect to if there is an error during the link process" }).optional(), + disableRedirect: z.boolean().meta({ description: "Disable automatic redirection to the provider. Useful for handling the redirection yourself" }).optional(), + additionalData: z.record(z.string(), z.any()).optional() + }), + use: [sessionMiddleware], + metadata: { openapi: { + description: "Link a social account to the user", + operationId: "linkSocialAccount", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + url: { + type: "string", + description: "The authorization URL to redirect the user to" + }, + redirect: { + type: "boolean", + description: "Indicates if the user should be redirected to the authorization URL" + }, + status: { type: "boolean" } + }, + required: ["redirect"] + } } } + } } + } } +}, async (c) => { + const session = c.context.session; + const provider = c.context.socialProviders.find((p) => p.id === c.body.provider); + if (!provider) { + c.context.logger.error("Provider not found. Make sure to add the provider in your auth config", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.PROVIDER_NOT_FOUND }); + } + if (c.body.idToken) { + if (!provider.verifyIdToken) { + c.context.logger.error("Provider does not support id token verification", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.ID_TOKEN_NOT_SUPPORTED }); + } + const { token, nonce } = c.body.idToken; + if (!await provider.verifyIdToken(token, nonce)) { + c.context.logger.error("Invalid id token", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + } + const linkingUserInfo = await provider.getUserInfo({ + idToken: token, + accessToken: c.body.idToken.accessToken, + refreshToken: c.body.idToken.refreshToken + }); + if (!linkingUserInfo || !linkingUserInfo?.user) { + c.context.logger.error("Failed to get user info", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO }); + } + const linkingUserId = String(linkingUserInfo.user.id); + if (!linkingUserInfo.user.email) { + c.context.logger.error("User email not found", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.USER_EMAIL_NOT_FOUND }); + } + if ((await c.context.internalAdapter.findAccounts(session.user.id)).find((a) => a.providerId === provider.id && a.accountId === linkingUserId)) return c.json({ + url: "", + status: true, + redirect: false + }); + if (!c.context.options.account?.accountLinking?.trustedProviders?.includes(provider.id) && !linkingUserInfo.user.emailVerified || c.context.options.account?.accountLinking?.enabled === false) throw new APIError("UNAUTHORIZED", { message: "Account not linked - linking not allowed" }); + if (linkingUserInfo.user.email?.toLowerCase() !== session.user.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) throw new APIError("UNAUTHORIZED", { message: "Account not linked - different emails not allowed" }); + try { + await c.context.internalAdapter.createAccount({ + userId: session.user.id, + providerId: provider.id, + accountId: linkingUserId, + accessToken: c.body.idToken.accessToken, + idToken: token, + refreshToken: c.body.idToken.refreshToken, + scope: c.body.idToken.scopes?.join(",") + }); + } catch { + throw new APIError("EXPECTATION_FAILED", { message: "Account not linked - unable to create account" }); + } + if (c.context.options.account?.accountLinking?.updateUserInfoOnLink === true) try { + await c.context.internalAdapter.updateUser(session.user.id, { + name: linkingUserInfo.user?.name, + image: linkingUserInfo.user?.image + }); + } catch (e) { + console.warn("Could not update user - " + e.toString()); + } + return c.json({ + url: "", + status: true, + redirect: false + }); + } + const state = await generateState(c, { + userId: session.user.id, + email: session.user.email + }, c.body.additionalData); + const url = await provider.createAuthorizationURL({ + state: state.state, + codeVerifier: state.codeVerifier, + redirectURI: `${c.context.baseURL}/callback/${provider.id}`, + scopes: c.body.scopes + }); + if (!c.body.disableRedirect) c.setHeader("Location", url.toString()); + return c.json({ + url: url.toString(), + redirect: !c.body.disableRedirect + }); +}); +const unlinkAccount = createAuthEndpoint("/unlink-account", { + method: "POST", + body: z.object({ + providerId: z.string(), + accountId: z.string().optional() + }), + use: [freshSessionMiddleware], + metadata: { openapi: { + description: "Unlink an account", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const { providerId, accountId } = ctx.body; + const accounts = await ctx.context.internalAdapter.findAccounts(ctx.context.session.user.id); + if (accounts.length === 1 && !ctx.context.options.account?.accountLinking?.allowUnlinkingAll) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_UNLINK_LAST_ACCOUNT }); + const accountExist = accounts.find((account) => accountId ? account.accountId === accountId && account.providerId === providerId : account.providerId === providerId); + if (!accountExist) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.ACCOUNT_NOT_FOUND }); + await ctx.context.internalAdapter.deleteAccount(accountExist.id); + return ctx.json({ status: true }); +}); +const getAccessToken = createAuthEndpoint("/get-access-token", { + method: "POST", + body: z.object({ + providerId: z.string().meta({ description: "The provider ID for the OAuth provider" }), + accountId: z.string().meta({ description: "The account ID associated with the refresh token" }).optional(), + userId: z.string().meta({ description: "The user ID associated with the account" }).optional() + }), + metadata: { openapi: { + description: "Get a valid access token, doing a refresh if needed", + responses: { + 200: { + description: "A Valid access token", + content: { "application/json": { schema: { + type: "object", + properties: { + tokenType: { type: "string" }, + idToken: { type: "string" }, + accessToken: { type: "string" }, + accessTokenExpiresAt: { + type: "string", + format: "date-time" + } + } + } } } + }, + 400: { description: "Invalid refresh token or provider configuration" } + } + } } +}, async (ctx) => { + const { providerId, accountId, userId } = ctx.body || {}; + const req = ctx.request; + const session = await getSessionFromCtx(ctx); + if (req && !session) throw ctx.error("UNAUTHORIZED"); + const resolvedUserId = session?.user?.id || userId; + if (!resolvedUserId) throw ctx.error("UNAUTHORIZED"); + if (!ctx.context.socialProviders.find((p) => p.id === providerId)) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} is not supported.` }); + const accountData = await getAccountCookie(ctx); + let account = void 0; + if (accountData && providerId === accountData.providerId && (!accountId || accountData.id === accountId)) account = accountData; + else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.id === accountId && acc.providerId === providerId : acc.providerId === providerId); + if (!account) throw new APIError("BAD_REQUEST", { message: "Account not found" }); + const provider = ctx.context.socialProviders.find((p) => p.id === providerId); + if (!provider) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} not found.` }); + try { + let newTokens = null; + const accessTokenExpired = account.accessTokenExpiresAt && new Date(account.accessTokenExpiresAt).getTime() - Date.now() < 5e3; + if (account.refreshToken && accessTokenExpired && provider.refreshAccessToken) { + const refreshToken$1 = await decryptOAuthToken(account.refreshToken, ctx.context); + newTokens = await provider.refreshAccessToken(refreshToken$1); + const updatedData = { + accessToken: await setTokenUtil(newTokens.accessToken, ctx.context), + accessTokenExpiresAt: newTokens.accessTokenExpiresAt, + refreshToken: await setTokenUtil(newTokens.refreshToken, ctx.context), + refreshTokenExpiresAt: newTokens.refreshTokenExpiresAt + }; + let updatedAccount = null; + if (account.id) updatedAccount = await ctx.context.internalAdapter.updateAccount(account.id, updatedData); + if (ctx.context.options.account?.storeAccountCookie) await setAccountCookie(ctx, { + ...account, + ...updatedAccount ?? updatedData + }); + } + const accessTokenExpiresAt = (() => { + if (newTokens?.accessTokenExpiresAt) { + if (typeof newTokens.accessTokenExpiresAt === "string") return new Date(newTokens.accessTokenExpiresAt); + return newTokens.accessTokenExpiresAt; + } + if (account.accessTokenExpiresAt) { + if (typeof account.accessTokenExpiresAt === "string") return new Date(account.accessTokenExpiresAt); + return account.accessTokenExpiresAt; + } + })(); + const tokens = { + accessToken: newTokens?.accessToken ?? await decryptOAuthToken(account.accessToken ?? "", ctx.context), + accessTokenExpiresAt, + scopes: account.scope?.split(",") ?? [], + idToken: newTokens?.idToken ?? account.idToken ?? void 0 + }; + return ctx.json(tokens); + } catch (error2) { + throw new APIError("BAD_REQUEST", { + message: "Failed to get a valid access token", + cause: error2 + }); + } +}); +const refreshToken = createAuthEndpoint("/refresh-token", { + method: "POST", + body: z.object({ + providerId: z.string().meta({ description: "The provider ID for the OAuth provider" }), + accountId: z.string().meta({ description: "The account ID associated with the refresh token" }).optional(), + userId: z.string().meta({ description: "The user ID associated with the account" }).optional() + }), + metadata: { openapi: { + description: "Refresh the access token using a refresh token", + responses: { + 200: { + description: "Access token refreshed successfully", + content: { "application/json": { schema: { + type: "object", + properties: { + tokenType: { type: "string" }, + idToken: { type: "string" }, + accessToken: { type: "string" }, + refreshToken: { type: "string" }, + accessTokenExpiresAt: { + type: "string", + format: "date-time" + }, + refreshTokenExpiresAt: { + type: "string", + format: "date-time" + } + } + } } } + }, + 400: { description: "Invalid refresh token or provider configuration" } + } + } } +}, async (ctx) => { + const { providerId, accountId, userId } = ctx.body; + const req = ctx.request; + const session = await getSessionFromCtx(ctx); + if (req && !session) throw ctx.error("UNAUTHORIZED"); + const resolvedUserId = session?.user?.id || userId; + if (!resolvedUserId) throw new APIError("BAD_REQUEST", { message: `Either userId or session is required` }); + const provider = ctx.context.socialProviders.find((p) => p.id === providerId); + if (!provider) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} not found.` }); + if (!provider.refreshAccessToken) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} does not support token refreshing.` }); + let account = void 0; + const accountData = await getAccountCookie(ctx); + if (accountData && (!providerId || providerId === accountData?.providerId)) account = accountData; + else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.id === accountId && acc.providerId === providerId : acc.providerId === providerId); + if (!account) throw new APIError("BAD_REQUEST", { message: "Account not found" }); + let refreshToken$1 = void 0; + if (accountData && providerId === accountData.providerId) refreshToken$1 = accountData.refreshToken ?? void 0; + else refreshToken$1 = account.refreshToken ?? void 0; + if (!refreshToken$1) throw new APIError("BAD_REQUEST", { message: "Refresh token not found" }); + try { + const decryptedRefreshToken = await decryptOAuthToken(refreshToken$1, ctx.context); + const tokens = await provider.refreshAccessToken(decryptedRefreshToken); + if (account.id) { + const updateData = { + ...account || {}, + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") || account.scope, + idToken: tokens.idToken || account.idToken + }; + await ctx.context.internalAdapter.updateAccount(account.id, updateData); + } + if (accountData && providerId === accountData.providerId && ctx.context.options.account?.storeAccountCookie) await setAccountCookie(ctx, { + ...accountData, + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") || accountData.scope, + idToken: tokens.idToken || accountData.idToken + }); + return ctx.json({ + accessToken: tokens.accessToken, + refreshToken: tokens.refreshToken, + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") || account.scope, + idToken: tokens.idToken || account.idToken, + providerId: account.providerId, + accountId: account.accountId + }); + } catch (error2) { + throw new APIError("BAD_REQUEST", { + message: "Failed to refresh access token", + cause: error2 + }); + } +}); +const accountInfoQuerySchema = z.optional(z.object({ accountId: z.string().meta({ description: "The provider given account id for which to get the account info" }).optional() })); +const accountInfo = createAuthEndpoint("/account-info", { + method: "GET", + use: [sessionMiddleware], + metadata: { openapi: { + description: "Get the account info provided by the provider", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + user: { + type: "object", + properties: { + id: { type: "string" }, + name: { type: "string" }, + email: { type: "string" }, + image: { type: "string" }, + emailVerified: { type: "boolean" } + }, + required: ["id", "emailVerified"] + }, + data: { + type: "object", + properties: {}, + additionalProperties: true + } + }, + required: ["user", "data"], + additionalProperties: false + } } } + } } + } }, + query: accountInfoQuerySchema +}, async (ctx) => { + const providedAccountId = ctx.query?.accountId; + let account = void 0; + if (!providedAccountId) { + if (ctx.context.options.account?.storeAccountCookie) { + const accountData = await getAccountCookie(ctx); + if (accountData) account = accountData; + } + } else { + const accountData = await ctx.context.internalAdapter.findAccount(providedAccountId); + if (accountData) account = accountData; + } + if (!account || account.userId !== ctx.context.session.user.id) throw new APIError("BAD_REQUEST", { message: "Account not found" }); + const provider = ctx.context.socialProviders.find((p) => p.id === account.providerId); + if (!provider) throw new APIError("INTERNAL_SERVER_ERROR", { message: `Provider account provider is ${account.providerId} but it is not configured` }); + const tokens = await getAccessToken({ + ...ctx, + method: "POST", + body: { + accountId: account.id, + providerId: account.providerId + }, + returnHeaders: false, + returnStatus: false + }); + if (!tokens.accessToken) throw new APIError("BAD_REQUEST", { message: "Access token not found" }); + const info2 = await provider.getUserInfo({ + ...tokens, + accessToken: tokens.accessToken + }); + return ctx.json(info2); +}); +async function createEmailVerificationToken(secret, email, updateTo, expiresIn = 3600, extraPayload) { + return await signJWT({ + email: email.toLowerCase(), + updateTo, + ...extraPayload + }, secret, expiresIn); +} +async function sendVerificationEmailFn(ctx, user) { + if (!ctx.context.options.emailVerification?.sendVerificationEmail) { + ctx.context.logger.error("Verification email isn't enabled."); + throw new APIError("BAD_REQUEST", { message: "Verification email isn't enabled" }); + } + const token = await createEmailVerificationToken(ctx.context.secret, user.email, void 0, ctx.context.options.emailVerification?.expiresIn); + const callbackURL = ctx.body.callbackURL ? encodeURIComponent(ctx.body.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user, + url, + token + }, ctx.request)); +} +const sendVerificationEmail = createAuthEndpoint("/send-verification-email", { + method: "POST", + operationId: "sendVerificationEmail", + body: z.object({ + email: z.email().meta({ description: "The email to send the verification email to" }), + callbackURL: z.string().meta({ description: "The URL to use for email verification callback" }).optional() + }), + metadata: { openapi: { + operationId: "sendVerificationEmail", + description: "Send a verification email to the user", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + email: { + type: "string", + description: "The email to send the verification email to", + example: "user@example.com" + }, + callbackURL: { + type: "string", + description: "The URL to use for email verification callback", + example: "https://example.com/callback", + nullable: true + } + }, + required: ["email"] + } } } }, + responses: { + "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if the email was sent successfully", + example: true + } } + } } } + }, + "400": { + description: "Bad Request", + content: { "application/json": { schema: { + type: "object", + properties: { message: { + type: "string", + description: "Error message", + example: "Verification email isn't enabled" + } } + } } } + } + } + } } +}, async (ctx) => { + if (!ctx.context.options.emailVerification?.sendVerificationEmail) { + ctx.context.logger.error("Verification email isn't enabled."); + throw new APIError("BAD_REQUEST", { message: "Verification email isn't enabled" }); + } + const { email } = ctx.body; + const session = await getSessionFromCtx(ctx); + if (!session) { + const user = await ctx.context.internalAdapter.findUserByEmail(email); + if (!user || user.user.emailVerified) { + await createEmailVerificationToken(ctx.context.secret, email, void 0, ctx.context.options.emailVerification?.expiresIn); + return ctx.json({ status: true }); + } + await sendVerificationEmailFn(ctx, user.user); + return ctx.json({ status: true }); + } + if (session?.user.email !== email) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.EMAIL_MISMATCH }); + if (session?.user.emailVerified) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.EMAIL_ALREADY_VERIFIED }); + await sendVerificationEmailFn(ctx, session.user); + return ctx.json({ status: true }); +}); +const verifyEmail = createAuthEndpoint("/verify-email", { + method: "GET", + operationId: "verifyEmail", + query: z.object({ + token: z.string().meta({ description: "The token to verify the email" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional() + }), + use: [originCheck((ctx) => ctx.query.callbackURL)], + metadata: { openapi: { + description: "Verify the email of the user", + parameters: [{ + name: "token", + in: "query", + description: "The token to verify the email", + required: true, + schema: { type: "string" } + }, { + name: "callbackURL", + in: "query", + description: "The URL to redirect to after email verification", + required: false, + schema: { type: "string" } + }], + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + user: { + type: "object", + $ref: "#/components/schemas/User" + }, + status: { + type: "boolean", + description: "Indicates if the email was verified successfully" + } + }, + required: ["user", "status"] + } } } + } } + } } +}, async (ctx) => { + function redirectOnError(error2) { + if (ctx.query.callbackURL) { + if (ctx.query.callbackURL.includes("?")) throw ctx.redirect(`${ctx.query.callbackURL}&error=${error2}`); + throw ctx.redirect(`${ctx.query.callbackURL}?error=${error2}`); + } + throw new APIError("UNAUTHORIZED", { message: error2 }); + } + const { token } = ctx.query; + let jwt; + try { + jwt = await jwtVerify(token, new TextEncoder().encode(ctx.context.secret), { algorithms: ["HS256"] }); + } catch (e) { + if (e instanceof JWTExpired) return redirectOnError("token_expired"); + return redirectOnError("invalid_token"); + } + const parsed = z.object({ + email: z.email(), + updateTo: z.string().optional(), + requestType: z.string().optional() + }).parse(jwt.payload); + const user = await ctx.context.internalAdapter.findUserByEmail(parsed.email); + if (!user) return redirectOnError("user_not_found"); + if (parsed.updateTo) { + const session = await getSessionFromCtx(ctx); + if (session && session.user.email !== parsed.email) return redirectOnError("unauthorized"); + switch (parsed.requestType) { + case "change-email-confirmation": { + const newToken = await createEmailVerificationToken(ctx.context.secret, parsed.email, parsed.updateTo, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-verification" }); + const updateCallbackURL = ctx.query.callbackURL ? encodeURIComponent(ctx.query.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${newToken}&callbackURL=${updateCallbackURL}`; + if (ctx.context.options.emailVerification?.sendVerificationEmail) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: { + ...user.user, + email: parsed.updateTo + }, + url, + token: newToken + }, ctx.request)); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ status: true }); + } + case "change-email-verification": { + let activeSession = session; + if (!activeSession) { + const newSession = await ctx.context.internalAdapter.createSession(user.user.id); + if (!newSession) throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + activeSession = { + session: newSession, + user: user.user + }; + } + if (ctx.context.options.emailVerification?.onEmailVerification) await ctx.context.options.emailVerification.onEmailVerification(user.user, ctx.request); + const updatedUser$1 = await ctx.context.internalAdapter.updateUserByEmail(parsed.email, { + email: parsed.updateTo, + emailVerified: true + }); + if (ctx.context.options.emailVerification?.afterEmailVerification) await ctx.context.options.emailVerification.afterEmailVerification(updatedUser$1, ctx.request); + await setSessionCookie(ctx, { + session: activeSession.session, + user: { + ...activeSession.user, + email: parsed.updateTo, + emailVerified: true + } + }); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: parseUserOutput(ctx.context.options, updatedUser$1) + }); + } + default: { + let activeSession = session; + if (!activeSession) { + const newSession = await ctx.context.internalAdapter.createSession(user.user.id); + if (!newSession) throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + activeSession = { + session: newSession, + user: user.user + }; + } + const updatedUser$1 = await ctx.context.internalAdapter.updateUserByEmail(parsed.email, { + email: parsed.updateTo, + emailVerified: false + }); + const newToken = await createEmailVerificationToken(ctx.context.secret, parsed.updateTo); + const updateCallbackURL = ctx.query.callbackURL ? encodeURIComponent(ctx.query.callbackURL) : encodeURIComponent("/"); + if (ctx.context.options.emailVerification?.sendVerificationEmail) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: updatedUser$1, + url: `${ctx.context.baseURL}/verify-email?token=${newToken}&callbackURL=${updateCallbackURL}`, + token: newToken + }, ctx.request)); + await setSessionCookie(ctx, { + session: activeSession.session, + user: { + ...activeSession.user, + email: parsed.updateTo, + emailVerified: false + } + }); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: parseUserOutput(ctx.context.options, updatedUser$1) + }); + } + } + } + if (user.user.emailVerified) { + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: null + }); + } + if (ctx.context.options.emailVerification?.beforeEmailVerification) await ctx.context.options.emailVerification.beforeEmailVerification(user.user, ctx.request); + if (ctx.context.options.emailVerification?.onEmailVerification) await ctx.context.options.emailVerification.onEmailVerification(user.user, ctx.request); + const updatedUser = await ctx.context.internalAdapter.updateUserByEmail(parsed.email, { emailVerified: true }); + if (ctx.context.options.emailVerification?.afterEmailVerification) await ctx.context.options.emailVerification.afterEmailVerification(updatedUser, ctx.request); + if (ctx.context.options.emailVerification?.autoSignInAfterVerification) { + const currentSession = await getSessionFromCtx(ctx); + if (!currentSession || currentSession.user.email !== parsed.email) { + const session = await ctx.context.internalAdapter.createSession(user.user.id); + if (!session) throw new APIError("INTERNAL_SERVER_ERROR", { message: "Failed to create session" }); + await setSessionCookie(ctx, { + session, + user: { + ...user.user, + emailVerified: true + } + }); + } else await setSessionCookie(ctx, { + session: currentSession.session, + user: { + ...currentSession.user, + emailVerified: true + } + }); + } + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: null + }); +}); +async function handleOAuthUserInfo(c, opts) { + const { userInfo, account, callbackURL, disableSignUp, overrideUserInfo } = opts; + const dbUser = await c.context.internalAdapter.findOAuthUser(userInfo.email.toLowerCase(), account.accountId, account.providerId).catch((e) => { + logger.error("Better auth was unable to query your database.\nError: ", e); + const errorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`; + throw c.redirect(`${errorURL}?error=internal_server_error`); + }); + let user = dbUser?.user; + const isRegister = !user; + if (dbUser) { + const linkedAccount = dbUser.linkedAccount ?? dbUser.accounts.find((acc) => acc.providerId === account.providerId && acc.accountId === account.accountId); + if (!linkedAccount) { + const accountLinking = c.context.options.account?.accountLinking; + const trustedProviders = c.context.options.account?.accountLinking?.trustedProviders; + if (!(opts.isTrustedProvider || trustedProviders?.includes(account.providerId)) && !userInfo.emailVerified || accountLinking?.enabled === false || accountLinking?.disableImplicitLinking === true) { + if (isDevelopment()) logger.warn(`User already exist but account isn't linked to ${account.providerId}. To read more about how account linking works in Better Auth see https://www.better-auth.com/docs/concepts/users-accounts#account-linking.`); + return { + error: "account not linked", + data: null + }; + } + try { + await c.context.internalAdapter.linkAccount({ + providerId: account.providerId, + accountId: userInfo.id.toString(), + userId: dbUser.user.id, + accessToken: await setTokenUtil(account.accessToken, c.context), + refreshToken: await setTokenUtil(account.refreshToken, c.context), + idToken: account.idToken, + accessTokenExpiresAt: account.accessTokenExpiresAt, + refreshTokenExpiresAt: account.refreshTokenExpiresAt, + scope: account.scope + }); + } catch (e) { + logger.error("Unable to link account", e); + return { + error: "unable to link account", + data: null + }; + } + if (userInfo.emailVerified && !dbUser.user.emailVerified && userInfo.email.toLowerCase() === dbUser.user.email) await c.context.internalAdapter.updateUser(dbUser.user.id, { emailVerified: true }); + } else { + const freshTokens = c.context.options.account?.updateAccountOnSignIn !== false ? Object.fromEntries(Object.entries({ + idToken: account.idToken, + accessToken: await setTokenUtil(account.accessToken, c.context), + refreshToken: await setTokenUtil(account.refreshToken, c.context), + accessTokenExpiresAt: account.accessTokenExpiresAt, + refreshTokenExpiresAt: account.refreshTokenExpiresAt, + scope: account.scope + }).filter(([_, value]) => value !== void 0)) : {}; + if (c.context.options.account?.storeAccountCookie) await setAccountCookie(c, { + ...linkedAccount, + ...freshTokens + }); + if (Object.keys(freshTokens).length > 0) await c.context.internalAdapter.updateAccount(linkedAccount.id, freshTokens); + if (userInfo.emailVerified && !dbUser.user.emailVerified && userInfo.email.toLowerCase() === dbUser.user.email) await c.context.internalAdapter.updateUser(dbUser.user.id, { emailVerified: true }); + } + if (overrideUserInfo) { + const { id: _, ...restUserInfo } = userInfo; + user = await c.context.internalAdapter.updateUser(dbUser.user.id, { + ...restUserInfo, + email: userInfo.email.toLowerCase(), + emailVerified: userInfo.email.toLowerCase() === dbUser.user.email ? dbUser.user.emailVerified || userInfo.emailVerified : userInfo.emailVerified + }); + } + } else { + if (disableSignUp) return { + error: "signup disabled", + data: null, + isRegister: false + }; + try { + const { id: _, ...restUserInfo } = userInfo; + const accountData = { + accessToken: await setTokenUtil(account.accessToken, c.context), + refreshToken: await setTokenUtil(account.refreshToken, c.context), + idToken: account.idToken, + accessTokenExpiresAt: account.accessTokenExpiresAt, + refreshTokenExpiresAt: account.refreshTokenExpiresAt, + scope: account.scope, + providerId: account.providerId, + accountId: userInfo.id.toString() + }; + const { user: createdUser, account: createdAccount } = await c.context.internalAdapter.createOAuthUser({ + ...restUserInfo, + email: userInfo.email.toLowerCase() + }, accountData); + user = createdUser; + if (c.context.options.account?.storeAccountCookie) await setAccountCookie(c, createdAccount); + if (!userInfo.emailVerified && user && c.context.options.emailVerification?.sendOnSignUp && c.context.options.emailVerification?.sendVerificationEmail) { + const token = await createEmailVerificationToken(c.context.secret, user.email, void 0, c.context.options.emailVerification?.expiresIn); + const url = `${c.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + await c.context.runInBackgroundOrAwait(c.context.options.emailVerification.sendVerificationEmail({ + user, + url, + token + }, c.request)); + } + } catch (e) { + logger.error(e); + if (e instanceof APIError) return { + error: e.message, + data: null, + isRegister: false + }; + return { + error: "unable to create user", + data: null, + isRegister: false + }; + } + } + if (!user) return { + error: "unable to create user", + data: null, + isRegister: false + }; + const session = await c.context.internalAdapter.createSession(user.id); + if (!session) return { + error: "unable to create session", + data: null, + isRegister: false + }; + return { + data: { + session, + user + }, + error: null, + isRegister + }; +} +const HIDE_METADATA = { scope: "server" }; +const schema = z.object({ + code: z.string().optional(), + error: z.string().optional(), + device_id: z.string().optional(), + error_description: z.string().optional(), + state: z.string().optional(), + user: z.string().optional() +}); +const callbackOAuth = createAuthEndpoint("/callback/:id", { + method: ["GET", "POST"], + operationId: "handleOAuthCallback", + body: schema.optional(), + query: schema.optional(), + metadata: { + ...HIDE_METADATA, + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"] + } +}, async (c) => { + let queryOrBody; + const defaultErrorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`; + if (c.method === "POST") { + const postData = c.body ? schema.parse(c.body) : {}; + const queryData = c.query ? schema.parse(c.query) : {}; + const mergedData = schema.parse({ + ...postData, + ...queryData + }); + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(mergedData)) if (value !== void 0 && value !== null) params.set(key, String(value)); + const redirectURL = `${c.context.baseURL}/callback/${c.params.id}?${params.toString()}`; + throw c.redirect(redirectURL); + } + try { + if (c.method === "GET") queryOrBody = schema.parse(c.query); + else if (c.method === "POST") queryOrBody = schema.parse(c.body); + else throw new Error("Unsupported method"); + } catch (e) { + c.context.logger.error("INVALID_CALLBACK_REQUEST", e); + throw c.redirect(`${defaultErrorURL}?error=invalid_callback_request`); + } + const { code, error: error2, state, error_description, device_id, user: userData } = queryOrBody; + if (!state) { + c.context.logger.error("State not found", error2); + const url = `${defaultErrorURL}${defaultErrorURL.includes("?") ? "&" : "?"}state=state_not_found`; + throw c.redirect(url); + } + const { codeVerifier, callbackURL, link, errorURL, newUserURL, requestSignUp } = await parseState(c); + function redirectOnError(error$1, description) { + const baseURL = errorURL ?? defaultErrorURL; + const params = new URLSearchParams({ error: error$1 }); + if (description) params.set("error_description", description); + const url = `${baseURL}${baseURL.includes("?") ? "&" : "?"}${params.toString()}`; + throw c.redirect(url); + } + if (error2) redirectOnError(error2, error_description); + if (!code) { + c.context.logger.error("Code not found"); + throw redirectOnError("no_code"); + } + const provider = c.context.socialProviders.find((p) => p.id === c.params.id); + if (!provider) { + c.context.logger.error("Oauth provider with id", c.params.id, "not found"); + throw redirectOnError("oauth_provider_not_found"); + } + let tokens; + try { + tokens = await provider.validateAuthorizationCode({ + code, + codeVerifier, + deviceId: device_id, + redirectURI: `${c.context.baseURL}/callback/${provider.id}` + }); + } catch (e) { + c.context.logger.error("", e); + throw redirectOnError("invalid_code"); + } + if (!tokens) throw redirectOnError("invalid_code"); + const parsedUserData = userData ? safeJSONParse(userData) : null; + const userInfo = await provider.getUserInfo({ + ...tokens, + user: parsedUserData ?? void 0 + }).then((res) => res?.user); + if (!userInfo) { + c.context.logger.error("Unable to get user info"); + return redirectOnError("unable_to_get_user_info"); + } + if (!callbackURL) { + c.context.logger.error("No callback URL found"); + throw redirectOnError("no_callback_url"); + } + if (link) { + if (!c.context.options.account?.accountLinking?.trustedProviders?.includes(provider.id) && !userInfo.emailVerified || c.context.options.account?.accountLinking?.enabled === false) { + c.context.logger.error("Unable to link account - untrusted provider"); + return redirectOnError("unable_to_link_account"); + } + if (userInfo.email?.toLowerCase() !== link.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) return redirectOnError("email_doesn't_match"); + const existingAccount = await c.context.internalAdapter.findAccount(String(userInfo.id)); + if (existingAccount) { + if (existingAccount.userId.toString() !== link.userId.toString()) return redirectOnError("account_already_linked_to_different_user"); + const updateData = Object.fromEntries(Object.entries({ + accessToken: await setTokenUtil(tokens.accessToken, c.context), + refreshToken: await setTokenUtil(tokens.refreshToken, c.context), + idToken: tokens.idToken, + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") + }).filter(([_, value]) => value !== void 0)); + await c.context.internalAdapter.updateAccount(existingAccount.id, updateData); + } else if (!await c.context.internalAdapter.createAccount({ + userId: link.userId, + providerId: provider.id, + accountId: String(userInfo.id), + ...tokens, + accessToken: await setTokenUtil(tokens.accessToken, c.context), + refreshToken: await setTokenUtil(tokens.refreshToken, c.context), + scope: tokens.scopes?.join(",") + })) return redirectOnError("unable_to_link_account"); + let toRedirectTo$1; + try { + toRedirectTo$1 = callbackURL.toString(); + } catch { + toRedirectTo$1 = callbackURL; + } + throw c.redirect(toRedirectTo$1); + } + if (!userInfo.email) { + c.context.logger.error("Provider did not return email. This could be due to misconfiguration in the provider settings."); + return redirectOnError("email_not_found"); + } + const accountData = { + providerId: provider.id, + accountId: String(userInfo.id), + ...tokens, + scope: tokens.scopes?.join(",") + }; + const result = await handleOAuthUserInfo(c, { + userInfo: { + ...userInfo, + id: String(userInfo.id), + email: userInfo.email, + name: userInfo.name || userInfo.email + }, + account: accountData, + callbackURL, + disableSignUp: provider.disableImplicitSignUp && !requestSignUp || provider.options?.disableSignUp, + overrideUserInfo: provider.options?.overrideUserInfoOnSignIn + }); + if (result.error) { + c.context.logger.error(result.error.split(" ").join("_")); + return redirectOnError(result.error.split(" ").join("_")); + } + const { session, user } = result.data; + await setSessionCookie(c, { + session, + user + }); + let toRedirectTo; + try { + toRedirectTo = (result.isRegister ? newUserURL || callbackURL : callbackURL).toString(); + } catch { + toRedirectTo = result.isRegister ? newUserURL || callbackURL : callbackURL; + } + throw c.redirect(toRedirectTo); +}); +function sanitize(input) { + return input.replace(//g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/&(?!amp;|lt;|gt;|quot;|#39;|#x[0-9a-fA-F]+;|#[0-9]+;)/g, "&"); +} +const html = (options, code = "Unknown", description = null) => { + const custom = options.onAPIError?.customizeDefaultErrorPage; + return ` + + + + + Error + + + +
+${custom?.disableBackgroundGrid ? "" : ` +
+
+`} + +
+ ${custom?.disableCornerDecorations ? "" : ` + +
+
+ +
+
`} + +
+
+
+

+ ERROR +

+
+
+
+ +

+ Something went wrong +

+ +
+ + CODE: + + + ${sanitize(code)} + +
+ +

+ ${!description ? `We encountered an unexpected error. Please try again or return to the home page. If you're a developer, you can find more information about the error here.` : description} +

+
+ + +
+
+ +`; +}; +const error = createAuthEndpoint("/error", { + method: "GET", + metadata: { + ...HIDE_METADATA, + openapi: { + description: "Displays an error page", + responses: { "200": { + description: "Success", + content: { "text/html": { schema: { + type: "string", + description: "The HTML content of the error page" + } } } + } } + } + } +}, async (c) => { + const url = new URL(c.request?.url || ""); + const unsanitizedCode = url.searchParams.get("error") || "UNKNOWN"; + const unsanitizedDescription = url.searchParams.get("error_description") || null; + const safeCode = /^[\'A-Za-z0-9_-]+$/.test(unsanitizedCode) ? unsanitizedCode : "UNKNOWN"; + const safeDescription = unsanitizedDescription ? sanitize(unsanitizedDescription) : null; + const queryParams = new URLSearchParams(); + queryParams.set("error", safeCode); + if (unsanitizedDescription) queryParams.set("error_description", unsanitizedDescription); + const options = c.context.options; + const errorURL = options.onAPIError?.errorURL; + if (errorURL) return new Response(null, { + status: 302, + headers: { Location: `${errorURL}${errorURL.includes("?") ? "&" : "?"}${queryParams.toString()}` } + }); + if (isProduction && !options.onAPIError?.customizeDefaultErrorPage) return new Response(null, { + status: 302, + headers: { Location: `/?${queryParams.toString()}` } + }); + return new Response(html(c.context.options, safeCode, safeDescription), { headers: { "Content-Type": "text/html" } }); +}); +const ok = createAuthEndpoint("/ok", { + method: "GET", + metadata: { + ...HIDE_METADATA, + openapi: { + description: "Check if the API is working", + responses: { "200": { + description: "API is working", + content: { "application/json": { schema: { + type: "object", + properties: { ok: { + type: "boolean", + description: "Indicates if the API is working" + } }, + required: ["ok"] + } } } + } } + } + } +}, async (ctx) => { + return ctx.json({ ok: true }); +}); +async function validatePassword(ctx, data) { + const credentialAccount = (await ctx.context.internalAdapter.findAccounts(data.userId))?.find((account) => account.providerId === "credential"); + const currentPassword = credentialAccount?.password; + if (!credentialAccount || !currentPassword) return false; + return await ctx.context.password.verify({ + hash: currentPassword, + password: data.password + }); +} +async function checkPassword(userId, c) { + const credentialAccount = (await c.context.internalAdapter.findAccounts(userId))?.find((account) => account.providerId === "credential"); + const currentPassword = credentialAccount?.password; + if (!credentialAccount || !currentPassword || !c.body.password) throw new APIError("BAD_REQUEST", { message: "No password credential found" }); + if (!await c.context.password.verify({ + hash: currentPassword, + password: c.body.password + })) throw new APIError("BAD_REQUEST", { message: "Invalid password" }); + return true; +} +function redirectError(ctx, callbackURL, query) { + const url = callbackURL ? new URL(callbackURL, ctx.baseURL) : new URL(`${ctx.baseURL}/error`); + if (query) Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v)); + return url.href; +} +function redirectCallback(ctx, callbackURL, query) { + const url = new URL(callbackURL, ctx.baseURL); + if (query) Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v)); + return url.href; +} +const requestPasswordReset = createAuthEndpoint("/request-password-reset", { + method: "POST", + body: z.object({ + email: z.email().meta({ description: "The email address of the user to send a password reset email to" }), + redirectTo: z.string().meta({ description: "The URL to redirect the user to reset their password. If the token isn't valid or expired, it'll be redirected with a query parameter `?error=INVALID_TOKEN`. If the token is valid, it'll be redirected with a query parameter `?token=VALID_TOKEN" }).optional() + }), + metadata: { openapi: { + operationId: "requestPasswordReset", + description: "Send a password reset email to the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + status: { type: "boolean" }, + message: { type: "string" } + } + } } } + } } + } } +}, async (ctx) => { + if (!ctx.context.options.emailAndPassword?.sendResetPassword) { + ctx.context.logger.error("Reset password isn't enabled.Please pass an emailAndPassword.sendResetPassword function in your auth config!"); + throw new APIError("BAD_REQUEST", { message: "Reset password isn't enabled" }); + } + const { email, redirectTo } = ctx.body; + const user = await ctx.context.internalAdapter.findUserByEmail(email, { includeAccounts: true }); + if (!user) { + generateId(24); + await ctx.context.internalAdapter.findVerificationValue("dummy-verification-token"); + ctx.context.logger.error("Reset Password: User not found", { email }); + return ctx.json({ + status: true, + message: "If this email exists in our system, check your email for the reset link" + }); + } + const expiresAt = getDate(ctx.context.options.emailAndPassword.resetPasswordTokenExpiresIn || 3600 * 1, "sec"); + const verificationToken = generateId(24); + await ctx.context.internalAdapter.createVerificationValue({ + value: user.user.id, + identifier: `reset-password:${verificationToken}`, + expiresAt + }); + const callbackURL = redirectTo ? encodeURIComponent(redirectTo) : ""; + const url = `${ctx.context.baseURL}/reset-password/${verificationToken}?callbackURL=${callbackURL}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailAndPassword.sendResetPassword({ + user: user.user, + url, + token: verificationToken + }, ctx.request)); + return ctx.json({ + status: true, + message: "If this email exists in our system, check your email for the reset link" + }); +}); +const requestPasswordResetCallback = createAuthEndpoint("/reset-password/:token", { + method: "GET", + operationId: "forgetPasswordCallback", + query: z.object({ callbackURL: z.string().meta({ description: "The URL to redirect the user to reset their password" }) }), + use: [originCheck((ctx) => ctx.query.callbackURL)], + metadata: { openapi: { + operationId: "resetPasswordCallback", + description: "Redirects the user to the callback URL with the token", + parameters: [{ + name: "token", + in: "path", + required: true, + description: "The token to reset the password", + schema: { type: "string" } + }, { + name: "callbackURL", + in: "query", + required: true, + description: "The URL to redirect the user to reset their password", + schema: { type: "string" } + }], + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { token: { type: "string" } } + } } } + } } + } } +}, async (ctx) => { + const { token } = ctx.params; + const { callbackURL } = ctx.query; + if (!token || !callbackURL) throw ctx.redirect(redirectError(ctx.context, callbackURL, { error: "INVALID_TOKEN" })); + const verification = await ctx.context.internalAdapter.findVerificationValue(`reset-password:${token}`); + if (!verification || verification.expiresAt < /* @__PURE__ */ new Date()) throw ctx.redirect(redirectError(ctx.context, callbackURL, { error: "INVALID_TOKEN" })); + throw ctx.redirect(redirectCallback(ctx.context, callbackURL, { token })); +}); +const resetPassword = createAuthEndpoint("/reset-password", { + method: "POST", + operationId: "resetPassword", + query: z.object({ token: z.string().optional() }).optional(), + body: z.object({ + newPassword: z.string().meta({ description: "The new password to set" }), + token: z.string().meta({ description: "The token to reset the password" }).optional() + }), + metadata: { openapi: { + operationId: "resetPassword", + description: "Reset the password for a user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const token = ctx.body.token || ctx.query?.token; + if (!token) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + const { newPassword } = ctx.body; + const minLength = ctx.context.password?.config.minPasswordLength; + const maxLength = ctx.context.password?.config.maxPasswordLength; + if (newPassword.length < minLength) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + if (newPassword.length > maxLength) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + const id = `reset-password:${token}`; + const verification = await ctx.context.internalAdapter.findVerificationValue(id); + if (!verification || verification.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + const userId = verification.value; + const hashedPassword = await ctx.context.password.hash(newPassword); + if (!(await ctx.context.internalAdapter.findAccounts(userId)).find((ac) => ac.providerId === "credential")) await ctx.context.internalAdapter.createAccount({ + userId, + providerId: "credential", + password: hashedPassword, + accountId: userId + }); + else await ctx.context.internalAdapter.updatePassword(userId, hashedPassword); + await ctx.context.internalAdapter.deleteVerificationValue(verification.id); + if (ctx.context.options.emailAndPassword?.onPasswordReset) { + const user = await ctx.context.internalAdapter.findUserById(userId); + if (user) await ctx.context.options.emailAndPassword.onPasswordReset({ user }, ctx.request); + } + if (ctx.context.options.emailAndPassword?.revokeSessionsOnPasswordReset) await ctx.context.internalAdapter.deleteSessions(userId); + return ctx.json({ status: true }); +}); +const verifyPassword = createAuthEndpoint("/verify-password", { + method: "POST", + body: z.object({ password: z.string().meta({ description: "The password to verify" }) }), + metadata: { + scope: "server", + openapi: { + operationId: "verifyPassword", + description: "Verify the current user's password", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { type: "boolean" } } + } } } + } } + } + }, + use: [sensitiveSessionMiddleware] +}, async (ctx) => { + const { password } = ctx.body; + const session = ctx.context.session; + if (!await validatePassword(ctx, { + password, + userId: session.user.id + })) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + return ctx.json({ status: true }); +}); +const socialSignInBodySchema = z.object({ + callbackURL: z.string().meta({ description: "Callback URL to redirect to after the user has signed in" }).optional(), + newUserCallbackURL: z.string().optional(), + errorCallbackURL: z.string().meta({ description: "Callback URL to redirect to if an error happens" }).optional(), + provider: SocialProviderListEnum, + disableRedirect: z.boolean().meta({ description: "Disable automatic redirection to the provider. Useful for handling the redirection yourself" }).optional(), + idToken: z.optional(z.object({ + token: z.string().meta({ description: "ID token from the provider" }), + nonce: z.string().meta({ description: "Nonce used to generate the token" }).optional(), + accessToken: z.string().meta({ description: "Access token from the provider" }).optional(), + refreshToken: z.string().meta({ description: "Refresh token from the provider" }).optional(), + expiresAt: z.number().meta({ description: "Expiry date of the token" }).optional() + })), + scopes: z.array(z.string()).meta({ description: "Array of scopes to request from the provider. This will override the default scopes passed." }).optional(), + requestSignUp: z.boolean().meta({ description: "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider" }).optional(), + loginHint: z.string().meta({ description: "The login hint to use for the authorization code request" }).optional(), + additionalData: z.record(z.string(), z.any()).optional().meta({ description: "Additional data to be passed through the OAuth flow" }) +}); +const signInSocial = () => createAuthEndpoint("/sign-in/social", { + method: "POST", + operationId: "socialSignIn", + body: socialSignInBodySchema, + metadata: { + $Infer: { + body: {}, + returned: {} + }, + openapi: { + description: "Sign in with a social provider", + operationId: "socialSignIn", + responses: { "200": { + description: "Success - Returns either session details or redirect URL", + content: { "application/json": { schema: { + type: "object", + description: "Session response when idToken is provided", + properties: { + token: { type: "string" }, + user: { + type: "object", + $ref: "#/components/schemas/User" + }, + url: { type: "string" }, + redirect: { + type: "boolean", + enum: [false] + } + }, + required: [ + "redirect", + "token", + "user" + ] + } } } + } } + } + } +}, async (c) => { + const provider = c.context.socialProviders.find((p) => p.id === c.body.provider); + if (!provider) { + c.context.logger.error("Provider not found. Make sure to add the provider in your auth config", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.PROVIDER_NOT_FOUND }); + } + if (c.body.idToken) { + if (!provider.verifyIdToken) { + c.context.logger.error("Provider does not support id token verification", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.ID_TOKEN_NOT_SUPPORTED }); + } + const { token, nonce } = c.body.idToken; + if (!await provider.verifyIdToken(token, nonce)) { + c.context.logger.error("Invalid id token", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + } + const userInfo = await provider.getUserInfo({ + idToken: token, + accessToken: c.body.idToken.accessToken, + refreshToken: c.body.idToken.refreshToken + }); + if (!userInfo || !userInfo?.user) { + c.context.logger.error("Failed to get user info", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO }); + } + if (!userInfo.user.email) { + c.context.logger.error("User email not found", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.USER_EMAIL_NOT_FOUND }); + } + const data = await handleOAuthUserInfo(c, { + userInfo: { + ...userInfo.user, + email: userInfo.user.email, + id: String(userInfo.user.id), + name: userInfo.user.name || "", + image: userInfo.user.image, + emailVerified: userInfo.user.emailVerified || false + }, + account: { + providerId: provider.id, + accountId: String(userInfo.user.id), + accessToken: c.body.idToken.accessToken + }, + callbackURL: c.body.callbackURL, + disableSignUp: provider.disableImplicitSignUp && !c.body.requestSignUp || provider.disableSignUp + }); + if (data.error) throw new APIError("UNAUTHORIZED", { message: data.error }); + await setSessionCookie(c, data.data); + return c.json({ + redirect: false, + token: data.data.session.token, + url: void 0, + user: parseUserOutput(c.context.options, data.data.user) + }); + } + const { codeVerifier, state } = await generateState(c, void 0, c.body.additionalData); + const url = await provider.createAuthorizationURL({ + state, + codeVerifier, + redirectURI: `${c.context.baseURL}/callback/${provider.id}`, + scopes: c.body.scopes, + loginHint: c.body.loginHint + }); + if (!c.body.disableRedirect) c.setHeader("Location", url.toString()); + return c.json({ + url: url.toString(), + redirect: !c.body.disableRedirect + }); +}); +const signInEmail = () => createAuthEndpoint("/sign-in/email", { + method: "POST", + operationId: "signInEmail", + use: [formCsrfMiddleware], + body: z.object({ + email: z.string().meta({ description: "Email of the user" }), + password: z.string().meta({ description: "Password of the user" }), + callbackURL: z.string().meta({ description: "Callback URL to use as a redirect for email verification" }).optional(), + rememberMe: z.boolean().meta({ description: "If this is false, the session will not be remembered. Default is `true`." }).default(true).optional() + }), + metadata: { + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"], + $Infer: { + body: {}, + returned: {} + }, + openapi: { + operationId: "signInEmail", + description: "Sign in with email and password", + responses: { "200": { + description: "Success - Returns either session details or redirect URL", + content: { "application/json": { schema: { + type: "object", + description: "Session response when idToken is provided", + properties: { + redirect: { + type: "boolean", + enum: [false] + }, + token: { + type: "string", + description: "Session token" + }, + url: { + type: "string", + nullable: true + }, + user: { + type: "object", + $ref: "#/components/schemas/User" + } + }, + required: [ + "redirect", + "token", + "user" + ] + } } } + } } + } + } +}, async (ctx) => { + if (!ctx.context.options?.emailAndPassword?.enabled) { + ctx.context.logger.error("Email and password is not enabled. Make sure to enable it in the options on you `auth.ts` file. Check `https://better-auth.com/docs/authentication/email-password` for more!"); + throw new APIError("BAD_REQUEST", { message: "Email and password is not enabled" }); + } + const { email, password } = ctx.body; + if (!z.email().safeParse(email).success) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_EMAIL }); + const user = await ctx.context.internalAdapter.findUserByEmail(email, { includeAccounts: true }); + if (!user) { + await ctx.context.password.hash(password); + ctx.context.logger.error("User not found", { email }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + const credentialAccount = user.accounts.find((a) => a.providerId === "credential"); + if (!credentialAccount) { + await ctx.context.password.hash(password); + ctx.context.logger.error("Credential account not found", { email }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + const currentPassword = credentialAccount?.password; + if (!currentPassword) { + await ctx.context.password.hash(password); + ctx.context.logger.error("Password not found", { email }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + if (!await ctx.context.password.verify({ + hash: currentPassword, + password + })) { + ctx.context.logger.error("Invalid password"); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + if (ctx.context.options?.emailAndPassword?.requireEmailVerification && !user.user.emailVerified) { + if (!ctx.context.options?.emailVerification?.sendVerificationEmail) throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.EMAIL_NOT_VERIFIED }); + if (ctx.context.options?.emailVerification?.sendOnSignIn) { + const token = await createEmailVerificationToken(ctx.context.secret, user.user.email, void 0, ctx.context.options.emailVerification?.expiresIn); + const callbackURL = ctx.body.callbackURL ? encodeURIComponent(ctx.body.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: user.user, + url, + token + }, ctx.request)); + } + throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.EMAIL_NOT_VERIFIED }); + } + const session = await ctx.context.internalAdapter.createSession(user.user.id, ctx.body.rememberMe === false); + if (!session) { + ctx.context.logger.error("Failed to create session"); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + } + await setSessionCookie(ctx, { + session, + user: user.user + }, ctx.body.rememberMe === false); + if (ctx.body.callbackURL) ctx.setHeader("Location", ctx.body.callbackURL); + return ctx.json({ + redirect: !!ctx.body.callbackURL, + token: session.token, + url: ctx.body.callbackURL, + user: parseUserOutput(ctx.context.options, user.user) + }); +}); +const signOut = createAuthEndpoint("/sign-out", { + method: "POST", + operationId: "signOut", + requireHeaders: true, + metadata: { openapi: { + operationId: "signOut", + description: "Sign out the current user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { success: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const sessionCookieToken = await ctx.getSignedCookie(ctx.context.authCookies.sessionToken.name, ctx.context.secret); + if (sessionCookieToken) try { + await ctx.context.internalAdapter.deleteSession(sessionCookieToken); + } catch (e) { + ctx.context.logger.error("Failed to delete session from database", e); + } + deleteSessionCookie(ctx); + return ctx.json({ success: true }); +}); +const signUpEmailBodySchema = z.object({ + name: z.string(), + email: z.email(), + password: z.string().nonempty(), + image: z.string().optional(), + callbackURL: z.string().optional(), + rememberMe: z.boolean().optional() +}).and(z.record(z.string(), z.any())); +const signUpEmail = () => createAuthEndpoint("/sign-up/email", { + method: "POST", + operationId: "signUpWithEmailAndPassword", + use: [formCsrfMiddleware], + body: signUpEmailBodySchema, + metadata: { + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"], + $Infer: { + body: {}, + returned: {} + }, + openapi: { + operationId: "signUpWithEmailAndPassword", + description: "Sign up a user using email and password", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + name: { + type: "string", + description: "The name of the user" + }, + email: { + type: "string", + description: "The email of the user" + }, + password: { + type: "string", + description: "The password of the user" + }, + image: { + type: "string", + description: "The profile image URL of the user" + }, + callbackURL: { + type: "string", + description: "The URL to use for email verification callback" + }, + rememberMe: { + type: "boolean", + description: "If this is false, the session will not be remembered. Default is `true`." + } + }, + required: [ + "name", + "email", + "password" + ] + } } } }, + responses: { + "200": { + description: "Successfully created user", + content: { "application/json": { schema: { + type: "object", + properties: { + token: { + type: "string", + nullable: true, + description: "Authentication token for the session" + }, + user: { + type: "object", + properties: { + id: { + type: "string", + description: "The unique identifier of the user" + }, + email: { + type: "string", + format: "email", + description: "The email address of the user" + }, + name: { + type: "string", + description: "The name of the user" + }, + image: { + type: "string", + format: "uri", + nullable: true, + description: "The profile image URL of the user" + }, + emailVerified: { + type: "boolean", + description: "Whether the email has been verified" + }, + createdAt: { + type: "string", + format: "date-time", + description: "When the user was created" + }, + updatedAt: { + type: "string", + format: "date-time", + description: "When the user was last updated" + } + }, + required: [ + "id", + "email", + "name", + "emailVerified", + "createdAt", + "updatedAt" + ] + } + }, + required: ["user"] + } } } + }, + "422": { + description: "Unprocessable Entity. User already exists or failed to create user.", + content: { "application/json": { schema: { + type: "object", + properties: { message: { type: "string" } } + } } } + } + } + } + } +}, async (ctx) => { + return runWithTransaction(ctx.context.adapter, async () => { + if (!ctx.context.options.emailAndPassword?.enabled || ctx.context.options.emailAndPassword?.disableSignUp) throw new APIError("BAD_REQUEST", { message: "Email and password sign up is not enabled" }); + const body = ctx.body; + const { name, email, password, image, callbackURL: _callbackURL, rememberMe, ...rest } = body; + if (!z.email().safeParse(email).success) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_EMAIL }); + if (!password || typeof password !== "string") throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + const minPasswordLength = ctx.context.password.config.minPasswordLength; + if (password.length < minPasswordLength) { + ctx.context.logger.error("Password is too short"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + } + const maxPasswordLength = ctx.context.password.config.maxPasswordLength; + if (password.length > maxPasswordLength) { + ctx.context.logger.error("Password is too long"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + } + if ((await ctx.context.internalAdapter.findUserByEmail(email))?.user) { + ctx.context.logger.info(`Sign-up attempt for existing email: ${email}`); + throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL }); + } + const hash = await ctx.context.password.hash(password); + let createdUser; + try { + const data = parseUserInput(ctx.context.options, rest, "create"); + createdUser = await ctx.context.internalAdapter.createUser({ + email: email.toLowerCase(), + name, + image, + ...data, + emailVerified: false + }); + if (!createdUser) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER }); + } catch (e) { + if (isDevelopment()) ctx.context.logger.error("Failed to create user", e); + if (e instanceof APIError) throw e; + ctx.context.logger?.error("Failed to create user", e); + throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER }); + } + if (!createdUser) throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER }); + await ctx.context.internalAdapter.linkAccount({ + userId: createdUser.id, + providerId: "credential", + accountId: createdUser.id, + password: hash + }); + if (ctx.context.options.emailVerification?.sendOnSignUp ?? ctx.context.options.emailAndPassword.requireEmailVerification) { + const token = await createEmailVerificationToken(ctx.context.secret, createdUser.email, void 0, ctx.context.options.emailVerification?.expiresIn); + const callbackURL = body.callbackURL ? encodeURIComponent(body.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + if (ctx.context.options.emailVerification?.sendVerificationEmail) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: createdUser, + url, + token + }, ctx.request)); + } + if (ctx.context.options.emailAndPassword.autoSignIn === false || ctx.context.options.emailAndPassword.requireEmailVerification) return ctx.json({ + token: null, + user: parseUserOutput(ctx.context.options, createdUser) + }); + const session = await ctx.context.internalAdapter.createSession(createdUser.id, rememberMe === false); + if (!session) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + await setSessionCookie(ctx, { + session, + user: createdUser + }, rememberMe === false); + return ctx.json({ + token: session.token, + user: parseUserOutput(ctx.context.options, createdUser) + }); + }); +}); +const updateUserBodySchema = z.record(z.string().meta({ description: "Field name must be a string" }), z.any()); +const updateUser = () => createAuthEndpoint("/update-user", { + method: "POST", + operationId: "updateUser", + body: updateUserBodySchema, + use: [sessionMiddleware], + metadata: { + $Infer: { body: {} }, + openapi: { + operationId: "updateUser", + description: "Update the current user", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + name: { + type: "string", + description: "The name of the user" + }, + image: { + type: "string", + description: "The image of the user", + nullable: true + } + } + } } } }, + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { user: { + type: "object", + $ref: "#/components/schemas/User" + } } + } } } + } } + } + } +}, async (ctx) => { + const body = ctx.body; + if (typeof body !== "object" || Array.isArray(body)) throw new APIError("BAD_REQUEST", { message: "Body must be an object" }); + if (body.email) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.EMAIL_CAN_NOT_BE_UPDATED }); + const { name, image, ...rest } = body; + const session = ctx.context.session; + const additionalFields = parseUserInput(ctx.context.options, rest, "update"); + if (image === void 0 && name === void 0 && Object.keys(additionalFields).length === 0) throw new APIError("BAD_REQUEST", { message: "No fields to update" }); + const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, { + name, + image, + ...additionalFields + }) ?? { + ...session.user, + ...name !== void 0 && { name }, + ...image !== void 0 && { image }, + ...additionalFields + }; + await setSessionCookie(ctx, { + session: session.session, + user: updatedUser + }); + return ctx.json({ status: true }); +}); +const changePassword = createAuthEndpoint("/change-password", { + method: "POST", + operationId: "changePassword", + body: z.object({ + newPassword: z.string().meta({ description: "The new password to set" }), + currentPassword: z.string().meta({ description: "The current password is required" }), + revokeOtherSessions: z.boolean().meta({ description: "Must be a boolean value" }).optional() + }), + use: [sensitiveSessionMiddleware], + metadata: { openapi: { + operationId: "changePassword", + description: "Change the password of the user", + responses: { "200": { + description: "Password successfully changed", + content: { "application/json": { schema: { + type: "object", + properties: { + token: { + type: "string", + nullable: true, + description: "New session token if other sessions were revoked" + }, + user: { + type: "object", + properties: { + id: { + type: "string", + description: "The unique identifier of the user" + }, + email: { + type: "string", + format: "email", + description: "The email address of the user" + }, + name: { + type: "string", + description: "The name of the user" + }, + image: { + type: "string", + format: "uri", + nullable: true, + description: "The profile image URL of the user" + }, + emailVerified: { + type: "boolean", + description: "Whether the email has been verified" + }, + createdAt: { + type: "string", + format: "date-time", + description: "When the user was created" + }, + updatedAt: { + type: "string", + format: "date-time", + description: "When the user was last updated" + } + }, + required: [ + "id", + "email", + "name", + "emailVerified", + "createdAt", + "updatedAt" + ] + } + }, + required: ["user"] + } } } + } } + } } +}, async (ctx) => { + const { newPassword, currentPassword, revokeOtherSessions: revokeOtherSessions2 } = ctx.body; + const session = ctx.context.session; + const minPasswordLength = ctx.context.password.config.minPasswordLength; + if (newPassword.length < minPasswordLength) { + ctx.context.logger.error("Password is too short"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + } + const maxPasswordLength = ctx.context.password.config.maxPasswordLength; + if (newPassword.length > maxPasswordLength) { + ctx.context.logger.error("Password is too long"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + } + const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account$1) => account$1.providerId === "credential" && account$1.password); + if (!account || !account.password) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND }); + const passwordHash = await ctx.context.password.hash(newPassword); + if (!await ctx.context.password.verify({ + hash: account.password, + password: currentPassword + })) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + await ctx.context.internalAdapter.updateAccount(account.id, { password: passwordHash }); + let token = null; + if (revokeOtherSessions2) { + await ctx.context.internalAdapter.deleteSessions(session.user.id); + const newSession = await ctx.context.internalAdapter.createSession(session.user.id); + if (!newSession) throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_GET_SESSION }); + await setSessionCookie(ctx, { + session: newSession, + user: session.user + }); + token = newSession.token; + } + return ctx.json({ + token, + user: parseUserOutput(ctx.context.options, session.user) + }); +}); +const setPassword = createAuthEndpoint({ + method: "POST", + body: z.object({ newPassword: z.string().meta({ description: "The new password to set is required" }) }), + use: [sensitiveSessionMiddleware] +}, async (ctx) => { + const { newPassword } = ctx.body; + const session = ctx.context.session; + const minPasswordLength = ctx.context.password.config.minPasswordLength; + if (newPassword.length < minPasswordLength) { + ctx.context.logger.error("Password is too short"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + } + const maxPasswordLength = ctx.context.password.config.maxPasswordLength; + if (newPassword.length > maxPasswordLength) { + ctx.context.logger.error("Password is too long"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + } + const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account$1) => account$1.providerId === "credential" && account$1.password); + const passwordHash = await ctx.context.password.hash(newPassword); + if (!account) { + await ctx.context.internalAdapter.linkAccount({ + userId: session.user.id, + providerId: "credential", + accountId: session.user.id, + password: passwordHash + }); + return ctx.json({ status: true }); + } + throw new APIError("BAD_REQUEST", { message: "user already has a password" }); +}); +const deleteUser = createAuthEndpoint("/delete-user", { + method: "POST", + use: [sensitiveSessionMiddleware], + body: z.object({ + callbackURL: z.string().meta({ description: "The callback URL to redirect to after the user is deleted" }).optional(), + password: z.string().meta({ description: "The password of the user is required to delete the user" }).optional(), + token: z.string().meta({ description: "The token to delete the user is required" }).optional() + }), + metadata: { openapi: { + operationId: "deleteUser", + description: "Delete the user", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + callbackURL: { + type: "string", + description: "The callback URL to redirect to after the user is deleted" + }, + password: { + type: "string", + description: "The user's password. Required if session is not fresh" + }, + token: { + type: "string", + description: "The deletion verification token" + } + } + } } } }, + responses: { "200": { + description: "User deletion processed successfully", + content: { "application/json": { schema: { + type: "object", + properties: { + success: { + type: "boolean", + description: "Indicates if the operation was successful" + }, + message: { + type: "string", + enum: ["User deleted", "Verification email sent"], + description: "Status message of the deletion process" + } + }, + required: ["success", "message"] + } } } + } } + } } +}, async (ctx) => { + if (!ctx.context.options.user?.deleteUser?.enabled) { + ctx.context.logger.error("Delete user is disabled. Enable it in the options"); + throw new APIError("NOT_FOUND"); + } + const session = ctx.context.session; + if (ctx.body.password) { + const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account$1) => account$1.providerId === "credential" && account$1.password); + if (!account || !account.password) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND }); + if (!await ctx.context.password.verify({ + hash: account.password, + password: ctx.body.password + })) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + } + if (ctx.body.token) { + await deleteUserCallback({ + ...ctx, + query: { token: ctx.body.token } + }); + return ctx.json({ + success: true, + message: "User deleted" + }); + } + if (ctx.context.options.user.deleteUser?.sendDeleteAccountVerification) { + const token = generateRandomString(32, "0-9", "a-z"); + await ctx.context.internalAdapter.createVerificationValue({ + value: session.user.id, + identifier: `delete-account-${token}`, + expiresAt: new Date(Date.now() + (ctx.context.options.user.deleteUser?.deleteTokenExpiresIn || 3600 * 24) * 1e3) + }); + const url = `${ctx.context.baseURL}/delete-user/callback?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.user.deleteUser.sendDeleteAccountVerification({ + user: session.user, + url, + token + }, ctx.request)); + return ctx.json({ + success: true, + message: "Verification email sent" + }); + } + if (!ctx.body.password && ctx.context.sessionConfig.freshAge !== 0) { + const currentAge = new Date(session.session.createdAt).getTime(); + const freshAge = ctx.context.sessionConfig.freshAge * 1e3; + if (Date.now() - currentAge > freshAge * 1e3) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.SESSION_EXPIRED }); + } + const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete; + if (beforeDelete) await beforeDelete(session.user, ctx.request); + await ctx.context.internalAdapter.deleteUser(session.user.id); + await ctx.context.internalAdapter.deleteSessions(session.user.id); + deleteSessionCookie(ctx); + const afterDelete = ctx.context.options.user.deleteUser?.afterDelete; + if (afterDelete) await afterDelete(session.user, ctx.request); + return ctx.json({ + success: true, + message: "User deleted" + }); +}); +const deleteUserCallback = createAuthEndpoint("/delete-user/callback", { + method: "GET", + query: z.object({ + token: z.string().meta({ description: "The token to verify the deletion request" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after deletion" }).optional() + }), + use: [originCheck((ctx) => ctx.query.callbackURL)], + metadata: { openapi: { + description: "Callback to complete user deletion with verification token", + responses: { "200": { + description: "User successfully deleted", + content: { "application/json": { schema: { + type: "object", + properties: { + success: { + type: "boolean", + description: "Indicates if the deletion was successful" + }, + message: { + type: "string", + enum: ["User deleted"], + description: "Confirmation message" + } + }, + required: ["success", "message"] + } } } + } } + } } +}, async (ctx) => { + if (!ctx.context.options.user?.deleteUser?.enabled) { + ctx.context.logger.error("Delete user is disabled. Enable it in the options"); + throw new APIError("NOT_FOUND"); + } + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO }); + const token = await ctx.context.internalAdapter.findVerificationValue(`delete-account-${ctx.query.token}`); + if (!token || token.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + if (token.value !== session.user.id) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete; + if (beforeDelete) await beforeDelete(session.user, ctx.request); + await ctx.context.internalAdapter.deleteUser(session.user.id); + await ctx.context.internalAdapter.deleteSessions(session.user.id); + await ctx.context.internalAdapter.deleteAccounts(session.user.id); + await ctx.context.internalAdapter.deleteVerificationValue(token.id); + deleteSessionCookie(ctx); + const afterDelete = ctx.context.options.user.deleteUser?.afterDelete; + if (afterDelete) await afterDelete(session.user, ctx.request); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL || "/"); + return ctx.json({ + success: true, + message: "User deleted" + }); +}); +const changeEmail = createAuthEndpoint("/change-email", { + method: "POST", + body: z.object({ + newEmail: z.email().meta({ description: "The new email address to set must be a valid email address" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional() + }), + use: [sensitiveSessionMiddleware], + metadata: { openapi: { + operationId: "changeEmail", + responses: { + "200": { + description: "Email change request processed successfully", + content: { "application/json": { schema: { + type: "object", + properties: { + user: { + type: "object", + $ref: "#/components/schemas/User" + }, + status: { + type: "boolean", + description: "Indicates if the request was successful" + }, + message: { + type: "string", + enum: ["Email updated", "Verification email sent"], + description: "Status message of the email change process", + nullable: true + } + }, + required: ["status"] + } } } + }, + "422": { + description: "Unprocessable Entity. Email already exists", + content: { "application/json": { schema: { + type: "object", + properties: { message: { type: "string" } } + } } } + } + } + } } +}, async (ctx) => { + if (!ctx.context.options.user?.changeEmail?.enabled) { + ctx.context.logger.error("Change email is disabled."); + throw new APIError("BAD_REQUEST", { message: "Change email is disabled" }); + } + const newEmail = ctx.body.newEmail.toLowerCase(); + if (newEmail === ctx.context.session.user.email) { + ctx.context.logger.error("Email is the same"); + throw new APIError("BAD_REQUEST", { message: "Email is the same" }); + } + if (await ctx.context.internalAdapter.findUserByEmail(newEmail)) { + ctx.context.logger.error("Email already exists"); + throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL }); + } + if (ctx.context.session.user.emailVerified !== true && ctx.context.options.user.changeEmail.updateEmailWithoutVerification) { + await ctx.context.internalAdapter.updateUserByEmail(ctx.context.session.user.email, { email: newEmail }); + await setSessionCookie(ctx, { + session: ctx.context.session.session, + user: { + ...ctx.context.session.user, + email: newEmail + } + }); + if (ctx.context.options.emailVerification?.sendVerificationEmail) { + const token$1 = await createEmailVerificationToken(ctx.context.secret, newEmail, void 0, ctx.context.options.emailVerification?.expiresIn); + const url$1 = `${ctx.context.baseURL}/verify-email?token=${token$1}&callbackURL=${ctx.body.callbackURL || "/"}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: { + ...ctx.context.session.user, + email: newEmail + }, + url: url$1, + token: token$1 + }, ctx.request)); + } + return ctx.json({ status: true }); + } + if (ctx.context.session.user.emailVerified && (ctx.context.options.user.changeEmail.sendChangeEmailConfirmation || ctx.context.options.user.changeEmail.sendChangeEmailVerification)) { + const token$1 = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-confirmation" }); + const url$1 = `${ctx.context.baseURL}/verify-email?token=${token$1}&callbackURL=${ctx.body.callbackURL || "/"}`; + const sendFn = ctx.context.options.user.changeEmail.sendChangeEmailConfirmation || ctx.context.options.user.changeEmail.sendChangeEmailVerification; + if (sendFn) await ctx.context.runInBackgroundOrAwait(sendFn({ + user: ctx.context.session.user, + newEmail, + url: url$1, + token: token$1 + }, ctx.request)); + return ctx.json({ status: true }); + } + if (!ctx.context.options.emailVerification?.sendVerificationEmail) { + ctx.context.logger.error("Verification email isn't enabled."); + throw new APIError("BAD_REQUEST", { message: "Verification email isn't enabled" }); + } + const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-verification" }); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: { + ...ctx.context.session.user, + email: newEmail + }, + url, + token + }, ctx.request)); + return ctx.json({ status: true }); +}); +const defuReplaceArrays = createDefu((obj, key, value) => { + if (Array.isArray(obj[key]) && Array.isArray(value)) { + obj[key] = value; + return true; + } +}); +const hooksSourceWeakMap = /* @__PURE__ */ new WeakMap(); +function toAuthEndpoints(endpoints, ctx) { + const api = {}; + for (const [key, endpoint] of Object.entries(endpoints)) { + api[key] = async (context) => { + const run = async () => { + const authContext = await ctx; + let internalContext = { + ...context, + context: { + ...authContext, + returned: void 0, + responseHeaders: void 0, + session: null + }, + path: endpoint.path, + headers: context?.headers ? new Headers(context?.headers) : void 0 + }; + return runWithEndpointContext(internalContext, async () => { + const { beforeHooks, afterHooks } = getHooks(authContext); + const before = await runBeforeHooks(internalContext, beforeHooks); + if ("context" in before && before.context && typeof before.context === "object") { + const { headers, ...rest } = before.context; + if (headers) headers.forEach((value, key$1) => { + internalContext.headers.set(key$1, value); + }); + internalContext = defuReplaceArrays(rest, internalContext); + } else if (before) return context?.asResponse ? toResponse(before, { headers: context?.headers }) : context?.returnHeaders ? { + headers: context?.headers, + response: before + } : before; + internalContext.asResponse = false; + internalContext.returnHeaders = true; + internalContext.returnStatus = true; + const result = await runWithEndpointContext(internalContext, () => endpoint(internalContext)).catch((e) => { + if (e instanceof APIError) + return { + response: e, + status: e.statusCode, + headers: e.headers ? new Headers(e.headers) : null + }; + throw e; + }); + if (result && result instanceof Response) return result; + internalContext.context.returned = result.response; + internalContext.context.responseHeaders = result.headers; + const after = await runAfterHooks(internalContext, afterHooks); + if (after.response) result.response = after.response; + if (result.response instanceof APIError && shouldPublishLog(authContext.logger.level, "debug")) result.response.stack = result.response.errorStack; + if (result.response instanceof APIError && !context?.asResponse) throw result.response; + return context?.asResponse ? toResponse(result.response, { + headers: result.headers, + status: result.status + }) : context?.returnHeaders ? context?.returnStatus ? { + headers: result.headers, + response: result.response, + status: result.status + } : { + headers: result.headers, + response: result.response + } : context?.returnStatus ? { + response: result.response, + status: result.status + } : result.response; + }); + }; + if (await hasRequestState()) return run(); + else return runWithRequestState(/* @__PURE__ */ new WeakMap(), run); + }; + api[key].path = endpoint.path; + api[key].options = endpoint.options; + } + return api; +} +async function runBeforeHooks(context, hooks) { + let modifiedContext = {}; + for (const hook of hooks) { + let matched = false; + try { + matched = hook.matcher(context); + } catch (error2) { + const hookSource = hooksSourceWeakMap.get(hook.handler) ?? "unknown"; + context.context.logger.error(`An error occurred during ${hookSource} hook matcher execution:`, error2); + throw new APIError("INTERNAL_SERVER_ERROR", { message: `An error occurred during hook matcher execution. Check the logs for more details.` }); + } + if (matched) { + const result = await hook.handler({ + ...context, + returnHeaders: false + }).catch((e) => { + if (e instanceof APIError && shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack; + throw e; + }); + if (result && typeof result === "object") { + if ("context" in result && typeof result.context === "object") { + const { headers, ...rest } = result.context; + if (headers instanceof Headers) if (modifiedContext.headers) headers.forEach((value, key) => { + modifiedContext.headers?.set(key, value); + }); + else modifiedContext.headers = headers; + modifiedContext = defuReplaceArrays(rest, modifiedContext); + continue; + } + return result; + } + } + } + return { context: modifiedContext }; +} +async function runAfterHooks(context, hooks) { + for (const hook of hooks) if (hook.matcher(context)) { + const result = await hook.handler(context).catch((e) => { + if (e instanceof APIError) { + if (shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack; + return { + response: e, + headers: e.headers ? new Headers(e.headers) : null + }; + } + throw e; + }); + if (result.headers) result.headers.forEach((value, key) => { + if (!context.context.responseHeaders) context.context.responseHeaders = new Headers({ [key]: value }); + else if (key.toLowerCase() === "set-cookie") context.context.responseHeaders.append(key, value); + else context.context.responseHeaders.set(key, value); + }); + if (result.response) context.context.returned = result.response; + } + return { + response: context.context.returned, + headers: context.context.responseHeaders + }; +} +function getHooks(authContext) { + const plugins = authContext.options.plugins || []; + const beforeHooks = []; + const afterHooks = []; + const beforeHookHandler = authContext.options.hooks?.before; + if (beforeHookHandler) { + hooksSourceWeakMap.set(beforeHookHandler, "user"); + beforeHooks.push({ + matcher: () => true, + handler: beforeHookHandler + }); + } + const afterHookHandler = authContext.options.hooks?.after; + if (afterHookHandler) { + hooksSourceWeakMap.set(afterHookHandler, "user"); + afterHooks.push({ + matcher: () => true, + handler: afterHookHandler + }); + } + const pluginBeforeHooks = plugins.filter((plugin) => plugin.hooks?.before).map((plugin) => plugin.hooks?.before).flat(); + const pluginAfterHooks = plugins.filter((plugin) => plugin.hooks?.after).map((plugin) => plugin.hooks?.after).flat(); + if (pluginBeforeHooks.length) beforeHooks.push(...pluginBeforeHooks); + if (pluginAfterHooks.length) afterHooks.push(...pluginAfterHooks); + return { + beforeHooks, + afterHooks + }; +} +function checkEndpointConflicts(options, logger$1) { + const endpointRegistry = /* @__PURE__ */ new Map(); + options.plugins?.forEach((plugin) => { + if (plugin.endpoints) { + for (const [key, endpoint] of Object.entries(plugin.endpoints)) if (endpoint && "path" in endpoint && typeof endpoint.path === "string") { + const path = endpoint.path; + let methods = []; + if (endpoint.options && "method" in endpoint.options) { + if (Array.isArray(endpoint.options.method)) methods = endpoint.options.method; + else if (typeof endpoint.options.method === "string") methods = [endpoint.options.method]; + } + if (methods.length === 0) methods = ["*"]; + if (!endpointRegistry.has(path)) endpointRegistry.set(path, []); + endpointRegistry.get(path).push({ + pluginId: plugin.id, + endpointKey: key, + methods + }); + } + } + }); + const conflicts = []; + for (const [path, entries] of endpointRegistry.entries()) if (entries.length > 1) { + const methodMap = /* @__PURE__ */ new Map(); + let hasConflict = false; + for (const entry of entries) for (const method of entry.methods) { + if (!methodMap.has(method)) methodMap.set(method, []); + methodMap.get(method).push(entry.pluginId); + if (methodMap.get(method).length > 1) hasConflict = true; + if (method === "*" && entries.length > 1) hasConflict = true; + else if (method !== "*" && methodMap.has("*")) hasConflict = true; + } + if (hasConflict) { + const uniquePlugins = [...new Set(entries.map((e) => e.pluginId))]; + const conflictingMethods = []; + for (const [method, plugins] of methodMap.entries()) if (plugins.length > 1 || method === "*" && entries.length > 1 || method !== "*" && methodMap.has("*")) conflictingMethods.push(method); + conflicts.push({ + path, + plugins: uniquePlugins, + conflictingMethods + }); + } + } + if (conflicts.length > 0) { + const conflictMessages = conflicts.map((conflict) => ` - "${conflict.path}" [${conflict.conflictingMethods.join(", ")}] used by plugins: ${conflict.plugins.join(", ")}`).join("\n"); + logger$1.error(`Endpoint path conflicts detected! Multiple plugins are trying to use the same endpoint paths with conflicting HTTP methods: +${conflictMessages} + +To resolve this, you can: + 1. Use only one of the conflicting plugins + 2. Configure the plugins to use different paths (if supported) + 3. Ensure plugins use different HTTP methods for the same path +`); + } +} +function getEndpoints(ctx, options) { + const pluginEndpoints = options.plugins?.reduce((acc, plugin) => { + return { + ...acc, + ...plugin.endpoints + }; + }, {}) ?? {}; + const middlewares = options.plugins?.map((plugin) => plugin.middlewares?.map((m) => { + const middleware = (async (context) => { + const authContext = await ctx; + return m.middleware({ + ...context, + context: { + ...authContext, + ...context.context + } + }); + }); + middleware.options = m.middleware.options; + return { + path: m.path, + middleware + }; + })).filter((plugin) => plugin !== void 0).flat() || []; + return { + api: toAuthEndpoints({ + signInSocial: signInSocial(), + callbackOAuth, + getSession: getSession(), + signOut, + signUpEmail: signUpEmail(), + signInEmail: signInEmail(), + resetPassword, + verifyPassword, + verifyEmail, + sendVerificationEmail, + changeEmail, + changePassword, + setPassword, + updateUser: updateUser(), + deleteUser, + requestPasswordReset, + requestPasswordResetCallback, + listSessions: listSessions(), + revokeSession, + revokeSessions, + revokeOtherSessions, + linkSocialAccount, + listUserAccounts, + deleteUserCallback, + unlinkAccount, + refreshToken, + getAccessToken, + accountInfo, + ...pluginEndpoints, + ok, + error + }, ctx), + middlewares + }; +} +const router = (ctx, options) => { + const { api, middlewares } = getEndpoints(ctx, options); + const basePath = new URL(ctx.baseURL).pathname; + return createRouter(api, { + routerContext: ctx, + openapi: { disabled: true }, + basePath, + routerMiddleware: [{ + path: "/**", + middleware: originCheckMiddleware + }, ...middlewares], + allowedMediaTypes: ["application/json"], + skipTrailingSlashes: options.advanced?.skipTrailingSlashes ?? false, + async onRequest(req) { + const disabledPaths = ctx.options.disabledPaths || []; + const normalizedPath = normalizePathname(req.url, basePath); + if (disabledPaths.includes(normalizedPath)) return new Response("Not Found", { status: 404 }); + let currentRequest = req; + for (const plugin of ctx.options.plugins || []) if (plugin.onRequest) { + const response = await plugin.onRequest(currentRequest, ctx); + if (response && "response" in response) return response.response; + if (response && "request" in response) currentRequest = response.request; + } + const rateLimitResponse2 = await onRequestRateLimit(currentRequest, ctx); + if (rateLimitResponse2) return rateLimitResponse2; + return currentRequest; + }, + async onResponse(res) { + for (const plugin of ctx.options.plugins || []) if (plugin.onResponse) { + const response = await plugin.onResponse(res, ctx); + if (response) return response.response; + } + return res; + }, + onError(e) { + if (e instanceof APIError && e.status === "FOUND") return; + if (options.onAPIError?.throw) throw e; + if (options.onAPIError?.onError) { + options.onAPIError.onError(e, ctx); + return; + } + const optLogLevel = options.logger?.level; + const log = optLogLevel === "error" || optLogLevel === "warn" || optLogLevel === "debug" ? logger : void 0; + if (options.logger?.disabled !== true) { + if (e && typeof e === "object" && "message" in e && typeof e.message === "string") { + if (e.message.includes("no column") || e.message.includes("column") || e.message.includes("relation") || e.message.includes("table") || e.message.includes("does not exist")) { + ctx.logger?.error(e.message); + return; + } + } + if (e instanceof APIError) { + if (e.status === "INTERNAL_SERVER_ERROR") ctx.logger.error(e.status, e); + log?.error(e.message); + } else ctx.logger?.error(e && typeof e === "object" && "name" in e ? e.name : "", e); + } + } + }); +}; +async function runPluginInit(ctx) { + let options = ctx.options; + const plugins = options.plugins || []; + let context = ctx; + const dbHooks = []; + for (const plugin of plugins) if (plugin.init) { + const initPromise = plugin.init(context); + let result; + if (isPromise(initPromise)) result = await initPromise; + else result = initPromise; + if (typeof result === "object") { + if (result.options) { + const { databaseHooks, ...restOpts } = result.options; + if (databaseHooks) dbHooks.push(databaseHooks); + options = defu(options, restOpts); + } + if (result.context) context = { + ...context, + ...result.context + }; + } + } + dbHooks.push(options.databaseHooks); + context.internalAdapter = createInternalAdapter(context.adapter, { + options, + logger: context.logger, + hooks: dbHooks.filter((u) => u !== void 0), + generateId: context.generateId + }); + context.options = options; + return { context }; +} +function getInternalPlugins(options) { + const plugins = []; + if (options.advanced?.crossSubDomainCookies?.enabled) ; + return plugins; +} +async function getTrustedOrigins(options, request) { + const baseURL = getBaseURL(options.baseURL, options.basePath, request); + const trustedOrigins = baseURL ? [new URL(baseURL).origin] : []; + if (options.trustedOrigins) { + if (Array.isArray(options.trustedOrigins)) trustedOrigins.push(...options.trustedOrigins); + if (typeof options.trustedOrigins === "function") { + const validOrigins = await options.trustedOrigins(request); + trustedOrigins.push(...validOrigins); + } + } + const envTrustedOrigins = env.BETTER_AUTH_TRUSTED_ORIGINS; + if (envTrustedOrigins) trustedOrigins.push(...envTrustedOrigins.split(",")); + return trustedOrigins.filter((v) => Boolean(v)); +} +const createBetterAuth = (options, initFn) => { + const authContext = initFn(options); + const { api } = getEndpoints(authContext, options); + return { + handler: async (request) => { + const ctx = await authContext; + const basePath = ctx.options.basePath || "/api/auth"; + if (!ctx.options.baseURL) { + const baseURL = getBaseURL(void 0, basePath, request, void 0, ctx.options.advanced?.trustedProxyHeaders); + if (baseURL) { + ctx.baseURL = baseURL; + ctx.options.baseURL = getOrigin(ctx.baseURL) || void 0; + } else throw new BetterAuthError("Could not get base URL from request. Please provide a valid base URL."); + } + ctx.trustedOrigins = await getTrustedOrigins(ctx.options, request); + const { handler } = router(ctx, options); + return runWithAdapter(ctx.adapter, () => handler(request)); + }, + api, + options, + $context: authContext, + $ERROR_CODES: { + ...options.plugins?.reduce((acc, plugin) => { + if (plugin.$ERROR_CODES) return { + ...acc, + ...plugin.$ERROR_CODES + }; + return acc; + }, {}), + ...BASE_ERROR_CODES + } + }; +}; +const DEFAULT_SECRET = "better-auth-secret-12345678901234567890"; +function estimateEntropy(str) { + const unique = new Set(str).size; + if (unique === 0) return 0; + return Math.log2(Math.pow(unique, str.length)); +} +function validateSecret(secret, logger$1) { + const isDefaultSecret = secret === DEFAULT_SECRET; + if (isTest()) return; + if (isDefaultSecret && isProduction) throw new BetterAuthError("You are using the default secret. Please set `BETTER_AUTH_SECRET` in your environment variables or pass `secret` in your auth config."); + if (secret.length < 32) logger$1.warn(`[better-auth] Warning: your BETTER_AUTH_SECRET should be at least 32 characters long for adequate security. Generate one with \`npx @better-auth/cli secret\` or \`openssl rand -base64 32\`.`); + if (estimateEntropy(secret) < 120) logger$1.warn("[better-auth] Warning: your BETTER_AUTH_SECRET appears low-entropy. Use a randomly generated secret for production."); +} +async function createAuthContext(adapter, options, getDatabaseType) { + if (!options.database) options = defu$1(options, { + session: { cookieCache: { + enabled: true, + strategy: "jwe", + refreshCache: true + } }, + account: { + storeStateStrategy: "cookie", + storeAccountCookie: true + } + }); + const plugins = options.plugins || []; + const internalPlugins = getInternalPlugins(options); + const logger$1 = createLogger(options.logger); + const baseURL = getBaseURL(options.baseURL, options.basePath); + if (!baseURL) logger$1.warn(`[better-auth] Base URL could not be determined. Please set a valid base URL using the baseURL config option or the BETTER_AUTH_URL environment variable. Without this, callbacks and redirects may not work correctly.`); + if (adapter.id === "memory" && options.advanced?.database?.generateId === false) logger$1.error(`[better-auth] Misconfiguration detected. +You are using the memory DB with generateId: false. +This will cause no id to be generated for any model. +Most of the features of Better Auth will not work correctly.`); + const secret = options.secret || env.BETTER_AUTH_SECRET || env.AUTH_SECRET || DEFAULT_SECRET; + validateSecret(secret, logger$1); + options = { + ...options, + secret, + baseURL: baseURL ? new URL(baseURL).origin : "", + basePath: options.basePath || "/api/auth", + plugins: plugins.concat(internalPlugins) + }; + checkEndpointConflicts(options, logger$1); + const cookies = getCookies(options); + const tables = getAuthTables(options); + const providers = Object.entries(options.socialProviders || {}).map(([key, config2]) => { + if (config2 == null) return null; + if (config2.enabled === false) return null; + if (!config2.clientId) logger$1.warn(`Social provider ${key} is missing clientId or clientSecret`); + const provider = socialProviders[key](config2); + provider.disableImplicitSignUp = config2.disableImplicitSignUp; + return provider; + }).filter((x) => x !== null); + const generateIdFunc = ({ model, size }) => { + if (typeof options.advanced?.generateId === "function") return options.advanced.generateId({ + model, + size + }); + const dbGenerateId = options?.advanced?.database?.generateId; + if (typeof dbGenerateId === "function") return dbGenerateId({ + model, + size + }); + if (dbGenerateId === "uuid") return crypto.randomUUID(); + if (dbGenerateId === "serial" || dbGenerateId === false) return false; + return generateId(size); + }; + const { publish } = await createTelemetry(options, { + adapter: adapter.id, + database: typeof options.database === "function" ? "adapter" : getDatabaseType(options.database) + }); + const trustedOrigins = await getTrustedOrigins(options); + const initOrPromise = runPluginInit({ + appName: options.appName || "Better Auth", + baseURL: baseURL || "", + version: getBetterAuthVersion(), + socialProviders: providers, + options, + oauthConfig: { + storeStateStrategy: options.account?.storeStateStrategy || (options.database ? "database" : "cookie"), + skipStateCookieCheck: !!options.account?.skipStateCookieCheck + }, + tables, + trustedOrigins, + isTrustedOrigin(url, settings) { + return this.trustedOrigins.some((origin) => matchesOriginPattern(url, origin, settings)); + }, + sessionConfig: { + updateAge: options.session?.updateAge !== void 0 ? options.session.updateAge : 1440 * 60, + expiresIn: options.session?.expiresIn || 3600 * 24 * 7, + freshAge: options.session?.freshAge === void 0 ? 3600 * 24 : options.session.freshAge, + cookieRefreshCache: (() => { + const refreshCache = options.session?.cookieCache?.refreshCache; + const maxAge = options.session?.cookieCache?.maxAge || 300; + if ((!!options.database || !!options.secondaryStorage) && refreshCache) { + logger$1.warn("[better-auth] `session.cookieCache.refreshCache` is enabled while `database` or `secondaryStorage` is configured. `refreshCache` is meant for stateless (DB-less) setups. Disabling `refreshCache` — remove it from your config to silence this warning."); + return false; + } + if (refreshCache === false || refreshCache === void 0) return false; + if (refreshCache === true) return { + enabled: true, + updateAge: Math.floor(maxAge * 0.2) + }; + return { + enabled: true, + updateAge: refreshCache.updateAge !== void 0 ? refreshCache.updateAge : Math.floor(maxAge * 0.2) + }; + })() + }, + secret, + rateLimit: { + ...options.rateLimit, + enabled: options.rateLimit?.enabled ?? isProduction, + window: options.rateLimit?.window || 10, + max: options.rateLimit?.max || 100, + storage: options.rateLimit?.storage || (options.secondaryStorage ? "secondary-storage" : "memory") + }, + authCookies: cookies, + logger: logger$1, + generateId: generateIdFunc, + session: null, + secondaryStorage: options.secondaryStorage, + password: { + hash: options.emailAndPassword?.password?.hash || hashPassword, + verify: options.emailAndPassword?.password?.verify || verifyPassword$1, + config: { + minPasswordLength: options.emailAndPassword?.minPasswordLength || 8, + maxPasswordLength: options.emailAndPassword?.maxPasswordLength || 128 + }, + checkPassword + }, + setNewSession(session) { + this.newSession = session; + }, + newSession: null, + adapter, + internalAdapter: createInternalAdapter(adapter, { + options, + logger: logger$1, + hooks: options.databaseHooks ? [options.databaseHooks] : [], + generateId: generateIdFunc + }), + createAuthCookie: createCookieGetter(options), + async runMigrations() { + throw new BetterAuthError("runMigrations will be set by the specific init implementation"); + }, + publishTelemetry: publish, + skipCSRFCheck: !!options.advanced?.disableCSRFCheck, + skipOriginCheck: options.advanced?.disableOriginCheck !== void 0 ? options.advanced.disableOriginCheck : isTest() ? true : false, + runInBackground: options.advanced?.backgroundTasks?.handler ?? ((p) => { + p.catch(() => { + }); + }), + async runInBackgroundOrAwait(promise) { + try { + if (options.advanced?.backgroundTasks?.handler) { + if (promise instanceof Promise) options.advanced.backgroundTasks.handler(promise.catch((e) => { + logger$1.error("Failed to run background task:", e); + })); + } else await promise; + } catch (e) { + logger$1.error("Failed to run background task:", e); + } + }, + getPlugin: (id) => options.plugins.find((p) => p.id === id) ?? null + }); + let context; + if (isPromise(initOrPromise)) ({ context } = await initOrPromise); + else ({ context } = initOrPromise); + if (typeof context.options.emailVerification?.onEmailVerification === "function") context.options.emailVerification.onEmailVerification = deprecate(context.options.emailVerification.onEmailVerification, "Use `afterEmailVerification` instead. This will be removed in 1.5", context.logger); + return context; +} +const initMinimal = async (options) => { + const adapter = await getBaseAdapter(options, async () => { + throw new BetterAuthError("Direct database connection requires Kysely. Please use `better-auth` instead of `better-auth/minimal`, or provide an adapter (drizzleAdapter, prismaAdapter, etc.)"); + }); + const getDatabaseType = (_database) => "unknown"; + const ctx = await createAuthContext(adapter, options, getDatabaseType); + ctx.runMigrations = async function() { + throw new BetterAuthError("Migrations are not supported in 'better-auth/minimal'. Please use 'better-auth' for migration support."); + }; + return ctx; +}; +const betterAuth = (options) => { + return createBetterAuth(options, initMinimal); +}; +const drizzleAdapter = (db2, config2) => { + let lazyOptions = null; + const createCustomAdapter = (db$1) => ({ getFieldName, getDefaultFieldName, options }) => { + function getSchema2(model) { + const schema2 = config2.schema || db$1._.fullSchema; + if (!schema2) throw new BetterAuthError("Drizzle adapter failed to initialize. Schema not found. Please provide a schema object in the adapter options object."); + const schemaModel = schema2[model]; + if (!schemaModel) throw new BetterAuthError(`[# Drizzle Adapter]: The model "${model}" was not found in the schema object. Please pass the schema directly to the adapter options.`); + return schemaModel; + } + const withReturning = async (model, builder, data, where) => { + if (config2.provider !== "mysql") return (await builder.returning())[0]; + await builder.execute(); + const schemaModel = getSchema2(model); + const builderVal = builder.config?.values; + if (where?.length) { + const clause = convertWhereClause(where.map((w) => { + if (data[w.field] !== void 0) return { + ...w, + value: data[w.field] + }; + return w; + }), model); + return (await db$1.select().from(schemaModel).where(...clause))[0]; + } else if (builderVal && builderVal[0]?.id?.value) { + let tId = builderVal[0]?.id?.value; + if (!tId) tId = (await db$1.select({ id: sql$1`LAST_INSERT_ID()` }).from(schemaModel).orderBy(desc(schemaModel.id)).limit(1))[0].id; + return (await db$1.select().from(schemaModel).where(eq(schemaModel.id, tId)).limit(1).execute())[0]; + } else if (data.id) return (await db$1.select().from(schemaModel).where(eq(schemaModel.id, data.id)).limit(1).execute())[0]; + else { + if (!("id" in schemaModel)) throw new BetterAuthError(`The model "${model}" does not have an "id" field. Please use the "id" field as your primary key.`); + return (await db$1.select().from(schemaModel).orderBy(desc(schemaModel.id)).limit(1).execute())[0]; + } + }; + function convertWhereClause(where, model) { + const schemaModel = getSchema2(model); + if (!where) return []; + if (where.length === 1) { + const w = where[0]; + if (!w) return []; + const field = getFieldName({ + model, + field: w.field + }); + if (!schemaModel[field]) throw new BetterAuthError(`The field "${w.field}" does not exist in the schema for the model "${model}". Please update your schema.`); + if (w.operator === "in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`); + return [inArray(schemaModel[field], w.value)]; + } + if (w.operator === "not_in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "not_in" operator.`); + return [notInArray(schemaModel[field], w.value)]; + } + if (w.operator === "contains") return [like(schemaModel[field], `%${w.value}%`)]; + if (w.operator === "starts_with") return [like(schemaModel[field], `${w.value}%`)]; + if (w.operator === "ends_with") return [like(schemaModel[field], `%${w.value}`)]; + if (w.operator === "lt") return [lt(schemaModel[field], w.value)]; + if (w.operator === "lte") return [lte(schemaModel[field], w.value)]; + if (w.operator === "ne") return [ne(schemaModel[field], w.value)]; + if (w.operator === "gt") return [gt(schemaModel[field], w.value)]; + if (w.operator === "gte") return [gte(schemaModel[field], w.value)]; + return [eq(schemaModel[field], w.value)]; + } + const andGroup = where.filter((w) => w.connector === "AND" || !w.connector); + const orGroup = where.filter((w) => w.connector === "OR"); + const andClause = and(...andGroup.map((w) => { + const field = getFieldName({ + model, + field: w.field + }); + if (w.operator === "in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`); + return inArray(schemaModel[field], w.value); + } + if (w.operator === "not_in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "not_in" operator.`); + return notInArray(schemaModel[field], w.value); + } + if (w.operator === "contains") return like(schemaModel[field], `%${w.value}%`); + if (w.operator === "starts_with") return like(schemaModel[field], `${w.value}%`); + if (w.operator === "ends_with") return like(schemaModel[field], `%${w.value}`); + if (w.operator === "lt") return lt(schemaModel[field], w.value); + if (w.operator === "lte") return lte(schemaModel[field], w.value); + if (w.operator === "gt") return gt(schemaModel[field], w.value); + if (w.operator === "gte") return gte(schemaModel[field], w.value); + if (w.operator === "ne") return ne(schemaModel[field], w.value); + return eq(schemaModel[field], w.value); + })); + const orClause = or(...orGroup.map((w) => { + const field = getFieldName({ + model, + field: w.field + }); + if (w.operator === "in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`); + return inArray(schemaModel[field], w.value); + } + if (w.operator === "not_in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "not_in" operator.`); + return notInArray(schemaModel[field], w.value); + } + if (w.operator === "contains") return like(schemaModel[field], `%${w.value}%`); + if (w.operator === "starts_with") return like(schemaModel[field], `${w.value}%`); + if (w.operator === "ends_with") return like(schemaModel[field], `%${w.value}`); + if (w.operator === "lt") return lt(schemaModel[field], w.value); + if (w.operator === "lte") return lte(schemaModel[field], w.value); + if (w.operator === "gt") return gt(schemaModel[field], w.value); + if (w.operator === "gte") return gte(schemaModel[field], w.value); + if (w.operator === "ne") return ne(schemaModel[field], w.value); + return eq(schemaModel[field], w.value); + })); + const clause = []; + if (andGroup.length) clause.push(andClause); + if (orGroup.length) clause.push(orClause); + return clause; + } + function checkMissingFields(schema2, model, values) { + if (!schema2) throw new BetterAuthError("Drizzle adapter failed to initialize. Drizzle Schema not found. Please provide a schema object in the adapter options object."); + for (const key in values) if (!schema2[key]) throw new BetterAuthError(`The field "${key}" does not exist in the "${model}" Drizzle schema. Please update your drizzle schema or re-generate using "npx @better-auth/cli@latest generate".`); + } + return { + async create({ model, data: values }) { + const schemaModel = getSchema2(model); + checkMissingFields(schemaModel, model, values); + return await withReturning(model, db$1.insert(schemaModel).values(values), values); + }, + async findOne({ model, where, select, join }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + if (options.experimental?.joins) if (!db$1.query || !db$1.query[model]) { + logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`); + logger.info("Falling back to regular query"); + } else { + let includes; + const pluralJoinResults = []; + if (join) { + includes = {}; + const joinEntries = Object.entries(join); + for (const [model$1, joinAttr] of joinEntries) { + const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100; + const isUnique = joinAttr.relation === "one-to-one"; + const pluralSuffix = isUnique || config2.usePlural ? "" : "s"; + includes[`${model$1}${pluralSuffix}`] = isUnique ? true : { limit }; + if (!isUnique) pluralJoinResults.push(`${model$1}${pluralSuffix}`); + } + } + const res$1 = await db$1.query[model].findFirst({ + where: clause[0], + columns: select?.length && select.length > 0 ? select.reduce((acc, field) => { + acc[getFieldName({ + model, + field + })] = true; + return acc; + }, {}) : void 0, + with: includes + }); + if (res$1) for (const pluralJoinResult of pluralJoinResults) { + const singularKey = !config2.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult; + res$1[singularKey] = res$1[pluralJoinResult]; + if (pluralJoinResult !== singularKey) delete res$1[pluralJoinResult]; + } + return res$1; + } + const res = await db$1.select(select?.length && select.length > 0 ? select.reduce((acc, field) => { + const fieldName = getFieldName({ + model, + field + }); + return { + ...acc, + [fieldName]: schemaModel[fieldName] + }; + }, {}) : void 0).from(schemaModel).where(...clause); + if (!res.length) return null; + return res[0]; + }, + async findMany({ model, where, sortBy, limit, select, offset, join }) { + const schemaModel = getSchema2(model); + const clause = where ? convertWhereClause(where, model) : []; + const sortFn = sortBy?.direction === "desc" ? desc : asc; + if (options.experimental?.joins) if (!db$1.query[model]) { + logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`); + logger.info("Falling back to regular query"); + } else { + let includes; + const pluralJoinResults = []; + if (join) { + includes = {}; + const joinEntries = Object.entries(join); + for (const [model$1, joinAttr] of joinEntries) { + const isUnique = joinAttr.relation === "one-to-one"; + const limit$1 = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100; + const pluralSuffix = isUnique || config2.usePlural ? "" : "s"; + includes[`${model$1}${pluralSuffix}`] = isUnique ? true : { limit: limit$1 }; + if (!isUnique) pluralJoinResults.push(`${model$1}${pluralSuffix}`); + } + } + let orderBy = void 0; + if (sortBy?.field) orderBy = [sortFn(schemaModel[getFieldName({ + model, + field: sortBy?.field + })])]; + const res = await db$1.query[model].findMany({ + where: clause[0], + with: includes, + columns: select?.length && select.length > 0 ? select.reduce((acc, field) => { + acc[getFieldName({ + model, + field + })] = true; + return acc; + }, {}) : void 0, + limit: limit ?? 100, + offset: offset ?? 0, + orderBy + }); + if (res) for (const item of res) for (const pluralJoinResult of pluralJoinResults) { + const singularKey = !config2.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult; + if (singularKey === pluralJoinResult) continue; + item[singularKey] = item[pluralJoinResult]; + delete item[pluralJoinResult]; + } + return res; + } + let builder = db$1.select(select?.length && select.length > 0 ? select.reduce((acc, field) => { + const fieldName = getFieldName({ + model, + field + }); + return { + ...acc, + [fieldName]: schemaModel[fieldName] + }; + }, {}) : void 0).from(schemaModel); + const effectiveLimit = limit; + const effectiveOffset = offset; + if (typeof effectiveLimit !== "undefined") builder = builder.limit(effectiveLimit); + if (typeof effectiveOffset !== "undefined") builder = builder.offset(effectiveOffset); + if (sortBy?.field) builder = builder.orderBy(sortFn(schemaModel[getFieldName({ + model, + field: sortBy?.field + })])); + return await builder.where(...clause); + }, + async count({ model, where }) { + const schemaModel = getSchema2(model); + const clause = where ? convertWhereClause(where, model) : []; + return (await db$1.select({ count: count() }).from(schemaModel).where(...clause))[0].count; + }, + async update({ model, where, update: values }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + return await withReturning(model, db$1.update(schemaModel).set(values).where(...clause), values, where); + }, + async updateMany({ model, where, update: values }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + return await db$1.update(schemaModel).set(values).where(...clause); + }, + async delete({ model, where }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + return await db$1.delete(schemaModel).where(...clause); + }, + async deleteMany({ model, where }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + const res = await db$1.delete(schemaModel).where(...clause); + let count$1 = 0; + if (res && "rowCount" in res) count$1 = res.rowCount; + else if (Array.isArray(res)) count$1 = res.length; + else if (res && ("affectedRows" in res || "rowsAffected" in res || "changes" in res)) count$1 = res.affectedRows ?? res.rowsAffected ?? res.changes; + if (typeof count$1 !== "number") logger.error("[Drizzle Adapter] The result of the deleteMany operation is not a number. This is likely a bug in the adapter. Please report this issue to the Better Auth team.", { + res, + model, + where + }); + return count$1; + }, + options: config2 + }; + }; + let adapterOptions = null; + adapterOptions = { + config: { + adapterId: "drizzle", + adapterName: "Drizzle Adapter", + usePlural: config2.usePlural ?? false, + debugLogs: config2.debugLogs ?? false, + supportsUUIDs: config2.provider === "pg" ? true : false, + supportsJSON: config2.provider === "pg" ? true : false, + supportsArrays: config2.provider === "pg" ? true : false, + transaction: config2.transaction ?? false ? (cb) => db2.transaction((tx) => { + return cb(createAdapterFactory({ + config: adapterOptions.config, + adapter: createCustomAdapter(tx) + })(lazyOptions)); + }) : false + }, + adapter: createCustomAdapter(db2) + }; + const adapter = createAdapterFactory(adapterOptions); + return (options) => { + lazyOptions = options; + return adapter(options); + }; +}; +const svelteKitHandler = async ({ auth: auth2, event, resolve, building }) => { + if (building) return resolve(event); + const { request, url } = event; + if (isAuthPath(url.toString(), auth2.options)) return auth2.handler(request); + return resolve(event); +}; +function isAuthPath(url, options) { + const _url = new URL(url); + const baseURL = new URL(`${options.baseURL || _url.origin}${options.basePath || "/api/auth"}`); + if (_url.origin !== baseURL.origin) return false; + if (!_url.pathname.startsWith(baseURL.pathname.endsWith("/") ? baseURL.pathname : `${baseURL.pathname}/`)) return false; + return true; +} +const sveltekitCookies = (getRequestEvent2) => { + return { + id: "sveltekit-cookies", + hooks: { after: [{ + matcher() { + return true; + }, + handler: createAuthMiddleware(async (ctx) => { + const returned = ctx.context.responseHeaders; + if ("_flag" in ctx && ctx._flag === "router") return; + if (returned instanceof Headers) { + const setCookies = returned?.get("set-cookie"); + if (!setCookies) return; + const event = getRequestEvent2(); + if (!event) return; + const parsed = parseSetCookieHeader(setCookies); + for (const [name, { value, ...ops }] of parsed) try { + event.cookies.set(name, decodeURIComponent(value), { + sameSite: ops.samesite, + path: ops.path || "/", + expires: ops.expires, + secure: ops.secure, + httpOnly: ops.httponly, + domain: ops.domain, + maxAge: ops["max-age"] + }); + } catch { + } + } + }) + }] } + }; +}; +defineErrorCodes({ + FAILED_TO_CREATE_USER: "Failed to create user", + USER_ALREADY_EXISTS: "User already exists.", + USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL: "User already exists. Use another email.", + YOU_CANNOT_BAN_YOURSELF: "You cannot ban yourself", + YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE: "You are not allowed to change users role", + YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS: "You are not allowed to create users", + YOU_ARE_NOT_ALLOWED_TO_LIST_USERS: "You are not allowed to list users", + YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS: "You are not allowed to list users sessions", + YOU_ARE_NOT_ALLOWED_TO_BAN_USERS: "You are not allowed to ban users", + YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS: "You are not allowed to impersonate users", + YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS: "You are not allowed to revoke users sessions", + YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS: "You are not allowed to delete users", + YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD: "You are not allowed to set users password", + BANNED_USER: "You have been banned from this application", + YOU_ARE_NOT_ALLOWED_TO_GET_USER: "You are not allowed to get user", + NO_DATA_TO_UPDATE: "No data to update", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS: "You are not allowed to update users", + YOU_CANNOT_REMOVE_YOURSELF: "You cannot remove yourself", + YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE: "You are not allowed to set a non-existent role value", + YOU_CANNOT_IMPERSONATE_ADMINS: "You cannot impersonate admins", + INVALID_ROLE_TYPE: "Invalid role type" +}); +createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +z.object({ + userId: z.coerce.string().meta({ description: "The user id" }), + role: z.union([z.string().meta({ description: "The role to set. `admin` or `user` by default" }), z.array(z.string().meta({ description: "The roles to set. `admin` or `user` by default" }))]).meta({ description: "The role to set, this can be a string or an array of strings. Eg: `admin` or `[admin, user]`" }) +}); +z.object({ id: z.string().meta({ description: "The id of the User" }) }); +z.object({ + email: z.string().meta({ description: "The email of the user" }), + password: z.string().optional().meta({ description: "The password of the user. If not provided, the user will be created without a credential account (useful for magic link or social login only users)." }), + name: z.string().meta({ description: "The name of the user" }), + role: z.union([z.string().meta({ description: "The role of the user" }), z.array(z.string().meta({ description: "The roles of user" }))]).optional().meta({ description: `A string or array of strings representing the roles to apply to the new user. Eg: "user"` }), + data: z.record(z.string(), z.any()).optional().meta({ description: "Extra fields for the user. Including custom additional fields." }) +}); +z.object({ + userId: z.coerce.string().meta({ description: "The user id" }), + data: z.record(z.any(), z.any()).meta({ description: "The user data to update" }) +}); +z.object({ + searchValue: z.string().optional().meta({ description: 'The value to search for. Eg: "some name"' }), + searchField: z.enum(["email", "name"]).meta({ description: 'The field to search in, defaults to email. Can be `email` or `name`. Eg: "name"' }).optional(), + searchOperator: z.enum([ + "contains", + "starts_with", + "ends_with" + ]).meta({ description: 'The operator to use for the search. Can be `contains`, `starts_with` or `ends_with`. Eg: "contains"' }).optional(), + limit: z.string().meta({ description: "The number of users to return" }).or(z.number()).optional(), + offset: z.string().meta({ description: "The offset to start from" }).or(z.number()).optional(), + sortBy: z.string().meta({ description: "The field to sort by" }).optional(), + sortDirection: z.enum(["asc", "desc"]).meta({ description: "The direction to sort by" }).optional(), + filterField: z.string().meta({ description: "The field to filter by" }).optional(), + filterValue: z.string().meta({ description: "The value to filter by" }).or(z.number()).or(z.boolean()).optional(), + filterOperator: z.enum([ + "eq", + "ne", + "lt", + "lte", + "gt", + "gte", + "contains" + ]).meta({ description: "The operator to use for the filter" }).optional() +}); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ + userId: z.coerce.string().meta({ description: "The user id" }), + banReason: z.string().meta({ description: "The reason for the ban" }).optional(), + banExpiresIn: z.number().meta({ description: "The number of seconds until the ban expires" }).optional() +}); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ sessionToken: z.string().meta({ description: "The session token" }) }); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ + newPassword: z.string().nonempty("newPassword cannot be empty").meta({ description: "The new password" }), + userId: z.coerce.string().nonempty("userId cannot be empty").meta({ description: "The user id" }) +}); +z.object({ + userId: z.coerce.string().optional().meta({ description: `The user id. Eg: "user-id"` }), + role: z.string().optional().meta({ description: `The role to check permission for. Eg: "admin"` }) +}).and(z.union([z.object({ + permission: z.record(z.string(), z.array(z.string())), + permissions: z.undefined() +}), z.object({ + permission: z.undefined(), + permissions: z.record(z.string(), z.array(z.string())) +})])); +defineErrorCodes({ + INVALID_EMAIL_FORMAT: "Email was not generated in a valid format", + FAILED_TO_CREATE_USER: "Failed to create user", + COULD_NOT_CREATE_SESSION: "Could not create session", + ANONYMOUS_USERS_CANNOT_SIGN_IN_AGAIN_ANONYMOUSLY: "Anonymous users cannot sign in again anonymously", + FAILED_TO_DELETE_ANONYMOUS_USER: "Failed to delete anonymous user", + USER_IS_NOT_ANONYMOUS: "User is not anonymous", + DELETE_ANONYMOUS_USER_DISABLED: "Deleting anonymous users is disabled" +}); +z.object({ + key: z.string().meta({ description: "The key to verify" }), + permissions: z.record(z.string(), z.array(z.string())).meta({ description: "The permissions to verify." }).optional() +}); +z.object({ + name: z.string().meta({ description: "Name of the Api Key" }).optional(), + expiresIn: z.number().meta({ description: "Expiration time of the Api Key in seconds" }).min(1).optional().nullable().default(null), + userId: z.coerce.string().meta({ description: 'User Id of the user that the Api Key belongs to. server-only. Eg: "user-id"' }).optional(), + prefix: z.string().meta({ description: "Prefix of the Api Key" }).regex(/^[a-zA-Z0-9_-]+$/, { message: "Invalid prefix format, must be alphanumeric and contain only underscores and hyphens." }).optional(), + remaining: z.number().meta({ description: "Remaining number of requests. Server side only" }).min(0).optional().nullable().default(null), + metadata: z.any().optional(), + refillAmount: z.number().meta({ description: "Amount to refill the remaining count of the Api Key. server-only. Eg: 100" }).min(1).optional(), + refillInterval: z.number().meta({ description: "Interval to refill the Api Key in milliseconds. server-only. Eg: 1000" }).optional(), + rateLimitTimeWindow: z.number().meta({ description: "The duration in milliseconds where each request is counted. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 1000" }).optional(), + rateLimitMax: z.number().meta({ description: "Maximum amount of requests allowed within a window. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 100" }).optional(), + rateLimitEnabled: z.boolean().meta({ description: "Whether the key has rate limiting enabled. server-only. Eg: true" }).optional(), + permissions: z.record(z.string(), z.array(z.string())).meta({ description: "Permissions of the Api Key." }).optional() +}); +z.object({ keyId: z.string().meta({ description: "The id of the Api Key" }) }); +z.object({ id: z.string().meta({ description: "The id of the Api Key" }) }); +z.object({ + keyId: z.string().meta({ description: "The id of the Api Key" }), + userId: z.coerce.string().meta({ description: 'The id of the user which the api key belongs to. server-only. Eg: "some-user-id"' }).optional(), + name: z.string().meta({ description: "The name of the key" }).optional(), + enabled: z.boolean().meta({ description: "Whether the Api Key is enabled or not" }).optional(), + remaining: z.number().meta({ description: "The number of remaining requests" }).min(1).optional(), + refillAmount: z.number().meta({ description: "The refill amount" }).optional(), + refillInterval: z.number().meta({ description: "The refill interval" }).optional(), + metadata: z.any().optional(), + expiresIn: z.number().meta({ description: "Expiration time of the Api Key in seconds" }).min(1).optional().nullable(), + rateLimitEnabled: z.boolean().meta({ description: "Whether the key has rate limiting enabled." }).optional(), + rateLimitTimeWindow: z.number().meta({ description: "The duration in milliseconds where each request is counted. server-only. Eg: 1000" }).optional(), + rateLimitMax: z.number().meta({ description: "Maximum amount of requests allowed within a window. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 100" }).optional(), + permissions: z.record(z.string(), z.array(z.string())).meta({ description: "Update the permissions on the API Key. server-only." }).optional().nullable() +}); +defineErrorCodes({ + INVALID_METADATA_TYPE: "metadata must be an object or undefined", + REFILL_AMOUNT_AND_INTERVAL_REQUIRED: "refillAmount is required when refillInterval is provided", + REFILL_INTERVAL_AND_AMOUNT_REQUIRED: "refillInterval is required when refillAmount is provided", + USER_BANNED: "User is banned", + UNAUTHORIZED_SESSION: "Unauthorized or invalid session", + KEY_NOT_FOUND: "API Key not found", + KEY_DISABLED: "API Key is disabled", + KEY_EXPIRED: "API Key has expired", + USAGE_EXCEEDED: "API Key has reached its usage limit", + KEY_NOT_RECOVERABLE: "API Key is not recoverable", + EXPIRES_IN_IS_TOO_SMALL: "The expiresIn is smaller than the predefined minimum value.", + EXPIRES_IN_IS_TOO_LARGE: "The expiresIn is larger than the predefined maximum value.", + INVALID_REMAINING: "The remaining count is either too large or too small.", + INVALID_PREFIX_LENGTH: "The prefix length is either too large or too small.", + INVALID_NAME_LENGTH: "The name length is either too large or too small.", + METADATA_DISABLED: "Metadata is disabled.", + RATE_LIMIT_EXCEEDED: "Rate limit exceeded.", + NO_VALUES_TO_UPDATE: "No values to update.", + KEY_DISABLED_EXPIRATION: "Custom key expiration values are disabled.", + INVALID_API_KEY: "Invalid API key.", + INVALID_USER_ID_FROM_API_KEY: "The user id from the API key is invalid.", + INVALID_API_KEY_GETTER_RETURN_TYPE: "API Key getter returned an invalid key type. Expected string.", + SERVER_ONLY_PROPERTY: "The property you're trying to set can only be set from the server auth instance only.", + FAILED_TO_UPDATE_API_KEY: "Failed to update API key", + NAME_REQUIRED: "API Key name is required." +}); +defineErrorCodes({ + VERIFICATION_FAILED: "Captcha verification failed", + MISSING_RESPONSE: "Missing CAPTCHA response", + UNKNOWN_ERROR: "Something went wrong" +}); +defineErrorCodes({ + MISSING_SECRET_KEY: "Missing secret key", + SERVICE_UNAVAILABLE: "CAPTCHA service unavailable" +}); +z.optional(z.object({ + disableCookieCache: z.boolean().meta({ description: "Disable cookie cache and fetch session from database" }).or(z.string().transform((v) => v === "true")).optional(), + disableRefresh: z.boolean().meta({ description: "Disable session refresh. Useful for checking session status, without updating the session" }).optional() +})); +const DEVICE_AUTHORIZATION_ERROR_CODES = defineErrorCodes({ + INVALID_DEVICE_CODE: "Invalid device code", + EXPIRED_DEVICE_CODE: "Device code has expired", + EXPIRED_USER_CODE: "User code has expired", + AUTHORIZATION_PENDING: "Authorization pending", + ACCESS_DENIED: "Access denied", + INVALID_USER_CODE: "Invalid user code", + DEVICE_CODE_ALREADY_PROCESSED: "Device code already processed", + POLLING_TOO_FREQUENTLY: "Polling too frequently", + USER_NOT_FOUND: "User not found", + FAILED_TO_CREATE_SESSION: "Failed to create session", + INVALID_DEVICE_CODE_STATUS: "Invalid device code status", + AUTHENTICATION_REQUIRED: "Authentication required" +}); +z.object({ + client_id: z.string().meta({ description: "The client ID of the application" }), + scope: z.string().meta({ description: "Space-separated list of scopes" }).optional() +}); +z.object({ + error: z.enum(["invalid_request", "invalid_client"]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) +}); +z.object({ + grant_type: z.literal("urn:ietf:params:oauth:grant-type:device_code").meta({ description: "The grant type for device flow" }), + device_code: z.string().meta({ description: "The device verification code" }), + client_id: z.string().meta({ description: "The client ID of the application" }) +}); +z.object({ + error: z.enum([ + "authorization_pending", + "slow_down", + "expired_token", + "access_denied", + "invalid_request", + "invalid_grant" + ]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) +}); +createAuthEndpoint("/device", { + method: "GET", + query: z.object({ user_code: z.string().meta({ description: "The user code to verify" }) }), + error: z.object({ + error: z.enum(["invalid_request"]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) + }), + metadata: { openapi: { + description: "Verify user code and get device authorization status", + responses: { 200: { + description: "Device authorization status", + content: { "application/json": { schema: { + type: "object", + properties: { + user_code: { + type: "string", + description: "The user code to verify" + }, + status: { + type: "string", + enum: [ + "pending", + "approved", + "denied" + ], + description: "Current status of the device authorization" + } + } + } } } + } } + } } +}, async (ctx) => { + const { user_code } = ctx.query; + const cleanUserCode = user_code.replace(/-/g, ""); + const deviceCodeRecord = await ctx.context.adapter.findOne({ + model: "deviceCode", + where: [{ + field: "userCode", + value: cleanUserCode + }] + }); + if (!deviceCodeRecord) throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE + }); + if (deviceCodeRecord.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { + error: "expired_token", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE + }); + return ctx.json({ + user_code, + status: deviceCodeRecord.status + }); +}); +createAuthEndpoint("/device/approve", { + method: "POST", + body: z.object({ userCode: z.string().meta({ description: "The user code to approve" }) }), + error: z.object({ + error: z.enum([ + "invalid_request", + "expired_token", + "device_code_already_processed", + "unauthorized", + "access_denied" + ]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) + }), + requireHeaders: true, + metadata: { openapi: { + description: "Approve device authorization", + responses: { 200: { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { success: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("UNAUTHORIZED", { + error: "unauthorized", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.AUTHENTICATION_REQUIRED + }); + const { userCode } = ctx.body; + const cleanUserCode = userCode.replace(/-/g, ""); + const deviceCodeRecord = await ctx.context.adapter.findOne({ + model: "deviceCode", + where: [{ + field: "userCode", + value: cleanUserCode + }] + }); + if (!deviceCodeRecord) throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE + }); + if (deviceCodeRecord.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { + error: "expired_token", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE + }); + if (deviceCodeRecord.status !== "pending") throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.DEVICE_CODE_ALREADY_PROCESSED + }); + if (deviceCodeRecord.userId && deviceCodeRecord.userId !== session.user.id) throw new APIError("FORBIDDEN", { + error: "access_denied", + error_description: "You are not authorized to approve this device authorization" + }); + await ctx.context.adapter.update({ + model: "deviceCode", + where: [{ + field: "id", + value: deviceCodeRecord.id + }], + update: { + status: "approved", + userId: session.user.id + } + }); + return ctx.json({ success: true }); +}); +createAuthEndpoint("/device/deny", { + method: "POST", + body: z.object({ userCode: z.string().meta({ description: "The user code to deny" }) }), + error: z.object({ + error: z.enum([ + "invalid_request", + "expired_token", + "unauthorized", + "access_denied" + ]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) + }), + requireHeaders: true, + metadata: { openapi: { + description: "Deny device authorization", + responses: { 200: { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { success: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("UNAUTHORIZED", { + error: "unauthorized", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.AUTHENTICATION_REQUIRED + }); + const { userCode } = ctx.body; + const cleanUserCode = userCode.replace(/-/g, ""); + const deviceCodeRecord = await ctx.context.adapter.findOne({ + model: "deviceCode", + where: [{ + field: "userCode", + value: cleanUserCode + }] + }); + if (!deviceCodeRecord) throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE + }); + if (deviceCodeRecord.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { + error: "expired_token", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE + }); + if (deviceCodeRecord.status !== "pending") throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.DEVICE_CODE_ALREADY_PROCESSED + }); + if (deviceCodeRecord.userId && deviceCodeRecord.userId !== session.user.id) throw new APIError("FORBIDDEN", { + error: "access_denied", + error_description: "You are not authorized to deny this device authorization" + }); + await ctx.context.adapter.update({ + model: "deviceCode", + where: [{ + field: "id", + value: deviceCodeRecord.id + }], + update: { + status: "denied", + userId: deviceCodeRecord.userId || session.user.id + } + }); + return ctx.json({ success: true }); +}); +z.object({ + id: z.string(), + deviceCode: z.string(), + userCode: z.string(), + userId: z.string().optional(), + expiresAt: z.date(), + status: z.string(), + lastPolledAt: z.date().optional(), + pollingInterval: z.number().optional(), + clientId: z.string().optional(), + scope: z.string().optional() +}); +const timeStringSchema = z.custom((val) => { + if (typeof val !== "string") return false; + try { + ms(val); + return true; + } catch { + return false; + } +}, { message: "Invalid time string format. Use formats like '30m', '5s', '1h', etc." }); +z.object({ + expiresIn: timeStringSchema.default("30m").describe("Time in seconds until the device code expires. Use formats like '30m', '5s', '1h', etc."), + interval: timeStringSchema.default("5s").describe("Time in seconds between polling attempts. Use formats like '30m', '5s', '1h', etc."), + deviceCodeLength: z.number().int().positive().default(40).describe("Length of the device code to be generated. Default is 40 characters."), + userCodeLength: z.number().int().positive().default(8).describe("Length of the user code to be generated. Default is 8 characters."), + generateDeviceCode: z.custom((val) => typeof val === "function", { message: "generateDeviceCode must be a function that returns a string or a promise that resolves to a string." }).optional().describe("Function to generate a device code. If not provided, a default random string generator will be used."), + generateUserCode: z.custom((val) => typeof val === "function", { message: "generateUserCode must be a function that returns a string or a promise that resolves to a string." }).optional().describe("Function to generate a user code. If not provided, a default random string generator will be used."), + validateClient: z.custom((val) => typeof val === "function", { message: "validateClient must be a function that returns a boolean or a promise that resolves to a boolean." }).optional().describe("Function to validate the client ID. If not provided, no validation will be performed."), + onDeviceAuthRequest: z.custom((val) => typeof val === "function", { message: "onDeviceAuthRequest must be a function that returns void or a promise that resolves to void." }).optional().describe("Function to handle device authorization requests. If not provided, no additional actions will be taken."), + verificationUri: z.string().optional().describe("The URI where users verify their device code. Can be an absolute URL (https://example.com/device) or relative path (/custom-path). This will be returned as verification_uri in the device code response. If not provided, defaults to /device."), + schema: z.custom(() => true) +}); +const types = [ + "email-verification", + "sign-in", + "forget-password" +]; +defineErrorCodes({ + OTP_EXPIRED: "OTP expired", + INVALID_OTP: "Invalid OTP", + TOO_MANY_ATTEMPTS: "Too many attempts" +}); +z.object({ + email: z.string({}).meta({ description: "Email address to send the OTP" }), + type: z.enum(types).meta({ description: "Type of the OTP" }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address to send the OTP" }), + type: z.enum(types).meta({ + required: true, + description: "Type of the OTP" + }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address the OTP was sent to" }), + type: z.enum(types).meta({ + required: true, + description: "Type of the OTP" + }) +}); +z.object({ + email: z.string().meta({ description: "Email address the OTP was sent to" }), + type: z.enum(types).meta({ + required: true, + description: "Type of the OTP" + }), + otp: z.string().meta({ + required: true, + description: "OTP to verify" + }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address to verify" }), + otp: z.string().meta({ + required: true, + description: "OTP to verify" + }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address to sign in" }), + otp: z.string().meta({ + required: true, + description: "OTP sent to the email" + }) +}); +z.object({ email: z.string().meta({ description: "Email address to send the OTP" }) }); +z.object({ email: z.string().meta({ description: "Email address to send the OTP" }) }); +z.object({ + email: z.string().meta({ description: "Email address to reset the password" }), + otp: z.string().meta({ description: "OTP sent to the email" }), + password: z.string().meta({ description: "New password" }) +}); +const GENERIC_OAUTH_ERROR_CODES = defineErrorCodes({ + INVALID_OAUTH_CONFIGURATION: "Invalid OAuth configuration", + TOKEN_URL_NOT_FOUND: "Invalid OAuth configuration. Token URL not found.", + PROVIDER_CONFIG_NOT_FOUND: "No config found for provider", + PROVIDER_ID_REQUIRED: "Provider ID is required", + INVALID_OAUTH_CONFIG: "Invalid OAuth configuration.", + SESSION_REQUIRED: "Session is required", + ISSUER_MISMATCH: "OAuth issuer mismatch. The authorization server issuer does not match the expected value (RFC 9207).", + ISSUER_MISSING: "OAuth issuer parameter missing. The authorization server did not include the required iss parameter (RFC 9207)." +}); +const signInWithOAuth2BodySchema = z.object({ + providerId: z.string().meta({ description: "The provider ID for the OAuth provider" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after sign in" }).optional(), + errorCallbackURL: z.string().meta({ description: "The URL to redirect to if an error occurs" }).optional(), + newUserCallbackURL: z.string().meta({ description: 'The URL to redirect to after login if the user is new. Eg: "/welcome"' }).optional(), + disableRedirect: z.boolean().meta({ description: "Disable redirect" }).optional(), + scopes: z.array(z.string()).meta({ description: "Scopes to be passed to the provider authorization request." }).optional(), + requestSignUp: z.boolean().meta({ description: "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider. Eg: false" }).optional(), + additionalData: z.record(z.string(), z.any()).optional() +}); +const signInWithOAuth2 = (options) => createAuthEndpoint("/sign-in/oauth2", { + method: "POST", + body: signInWithOAuth2BodySchema, + metadata: { openapi: { + description: "Sign in with OAuth2", + responses: { 200: { + description: "Sign in with OAuth2", + content: { "application/json": { schema: { + type: "object", + properties: { + url: { type: "string" }, + redirect: { type: "boolean" } + } + } } } + } } + } } +}, async (ctx) => { + const { providerId } = ctx.body; + const config2 = options.config.find((c) => c.providerId === providerId); + if (!config2) throw new APIError("BAD_REQUEST", { message: `${GENERIC_OAUTH_ERROR_CODES.PROVIDER_CONFIG_NOT_FOUND} ${providerId}` }); + const { discoveryUrl, authorizationUrl, tokenUrl, clientId, clientSecret, scopes, redirectURI, responseType, pkce, prompt, accessType, authorizationUrlParams, responseMode } = config2; + let finalAuthUrl = authorizationUrl; + let finalTokenUrl = tokenUrl; + if (discoveryUrl) { + const discovery = await betterFetch(discoveryUrl, { + method: "GET", + headers: config2.discoveryHeaders, + onError(context) { + ctx.context.logger.error(context.error.message, context.error, { discoveryUrl }); + } + }); + if (discovery.data) { + finalAuthUrl = discovery.data.authorization_endpoint; + finalTokenUrl = discovery.data.token_endpoint; + } + } + if (!finalAuthUrl || !finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + if (authorizationUrlParams) { + const withAdditionalParams = new URL(finalAuthUrl); + for (const [paramName, paramValue] of Object.entries(authorizationUrlParams)) withAdditionalParams.searchParams.set(paramName, paramValue); + finalAuthUrl = withAdditionalParams.toString(); + } + const additionalParams = typeof authorizationUrlParams === "function" ? authorizationUrlParams(ctx) : authorizationUrlParams; + const { state, codeVerifier } = await generateState(ctx, void 0, ctx.body.additionalData); + const authUrl = await createAuthorizationURL({ + id: providerId, + options: { + clientId, + clientSecret, + redirectURI + }, + authorizationEndpoint: finalAuthUrl, + state, + codeVerifier: pkce ? codeVerifier : void 0, + scopes: ctx.body.scopes ? [...ctx.body.scopes, ...scopes || []] : scopes || [], + redirectURI: `${ctx.context.baseURL}/oauth2/callback/${providerId}`, + prompt, + accessType, + responseType, + responseMode, + additionalParams + }); + return ctx.json({ + url: authUrl.toString(), + redirect: !ctx.body.disableRedirect + }); +}); +const OAuth2CallbackQuerySchema = z.object({ + code: z.string().meta({ description: "The OAuth2 code" }).optional(), + error: z.string().meta({ description: "The error message, if any" }).optional(), + error_description: z.string().meta({ description: "The error description, if any" }).optional(), + state: z.string().meta({ description: "The state parameter from the OAuth2 request" }).optional(), + iss: z.string().meta({ description: "The issuer identifier" }).optional() +}); +const oAuth2Callback = (options) => createAuthEndpoint("/oauth2/callback/:providerId", { + method: "GET", + query: OAuth2CallbackQuerySchema, + metadata: { + ...HIDE_METADATA, + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"], + openapi: { + description: "OAuth2 callback", + responses: { 200: { + description: "OAuth2 callback", + content: { "application/json": { schema: { + type: "object", + properties: { url: { type: "string" } } + } } } + } } + } + } +}, async (ctx) => { + const defaultErrorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`; + if (ctx.query.error || !ctx.query.code) throw ctx.redirect(`${defaultErrorURL}?error=${ctx.query.error || "oAuth_code_missing"}&error_description=${ctx.query.error_description}`); + const providerId = ctx.params?.providerId; + if (!providerId) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.PROVIDER_ID_REQUIRED }); + const providerConfig = options.config.find((p) => p.providerId === providerId); + if (!providerConfig) throw new APIError("BAD_REQUEST", { message: `${GENERIC_OAUTH_ERROR_CODES.PROVIDER_CONFIG_NOT_FOUND} ${providerId}` }); + let tokens = void 0; + const { callbackURL, codeVerifier, errorURL, requestSignUp, newUserURL, link } = await parseState(ctx); + const code = ctx.query.code; + function redirectOnError(error2) { + const defaultErrorURL$1 = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`; + let url = errorURL || defaultErrorURL$1; + if (url.includes("?")) url = `${url}&error=${error2}`; + else url = `${url}?error=${error2}`; + throw ctx.redirect(url); + } + let finalTokenUrl = providerConfig.tokenUrl; + let finalUserInfoUrl = providerConfig.userInfoUrl; + let expectedIssuer = providerConfig.issuer; + if (providerConfig.discoveryUrl) { + const discovery = await betterFetch(providerConfig.discoveryUrl, { + method: "GET", + headers: providerConfig.discoveryHeaders + }); + if (discovery.data) { + finalTokenUrl = discovery.data.token_endpoint; + finalUserInfoUrl = discovery.data.userinfo_endpoint; + if (!expectedIssuer && discovery.data.issuer) expectedIssuer = discovery.data.issuer; + } + } + if (expectedIssuer) { + if (ctx.query.iss) { + if (ctx.query.iss !== expectedIssuer) { + ctx.context.logger.error("OAuth issuer mismatch", { + expected: expectedIssuer, + received: ctx.query.iss + }); + return redirectOnError("issuer_mismatch"); + } + } else if (providerConfig.requireIssuerValidation) { + ctx.context.logger.error("OAuth issuer parameter missing", { expected: expectedIssuer }); + return redirectOnError("issuer_missing"); + } + } + try { + if (providerConfig.getToken) tokens = await providerConfig.getToken({ + code, + redirectURI: `${ctx.context.baseURL}/oauth2/callback/${providerConfig.providerId}`, + codeVerifier: providerConfig.pkce ? codeVerifier : void 0 + }); + else { + if (!finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIG }); + const additionalParams = typeof providerConfig.tokenUrlParams === "function" ? providerConfig.tokenUrlParams(ctx) : providerConfig.tokenUrlParams; + tokens = await validateAuthorizationCode({ + headers: providerConfig.authorizationHeaders, + code, + codeVerifier: providerConfig.pkce ? codeVerifier : void 0, + redirectURI: `${ctx.context.baseURL}/oauth2/callback/${providerConfig.providerId}`, + options: { + clientId: providerConfig.clientId, + clientSecret: providerConfig.clientSecret, + redirectURI: providerConfig.redirectURI + }, + tokenEndpoint: finalTokenUrl, + authentication: providerConfig.authentication, + additionalParams + }); + } + } catch (e) { + ctx.context.logger.error(e && typeof e === "object" && "name" in e ? e.name : "", e); + throw redirectOnError("oauth_code_verification_failed"); + } + if (!tokens) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIG }); + const userInfo = await (async function handleUserInfo() { + const userInfo$1 = providerConfig.getUserInfo ? await providerConfig.getUserInfo(tokens) : await getUserInfo(tokens, finalUserInfoUrl); + if (!userInfo$1) throw redirectOnError("user_info_is_missing"); + const mapUser = providerConfig.mapProfileToUser ? await providerConfig.mapProfileToUser(userInfo$1) : userInfo$1; + const email = mapUser.email ? mapUser.email.toLowerCase() : userInfo$1.email?.toLowerCase(); + if (!email) { + ctx.context.logger.error("Unable to get user info", userInfo$1); + throw redirectOnError("email_is_missing"); + } + const id = mapUser.id ? String(mapUser.id) : String(userInfo$1.id); + const name = mapUser.name ? mapUser.name : userInfo$1.name; + if (!name) { + ctx.context.logger.error("Unable to get user info", userInfo$1); + throw redirectOnError("name_is_missing"); + } + return { + ...userInfo$1, + ...mapUser, + email, + id, + name + }; + })(); + if (link) { + if (ctx.context.options.account?.accountLinking?.allowDifferentEmails !== true && link.email.toLowerCase() !== userInfo.email.toLowerCase()) return redirectOnError("email_doesn't_match"); + const existingAccount = await ctx.context.internalAdapter.findAccountByProviderId(String(userInfo.id), providerConfig.providerId); + if (existingAccount) { + if (existingAccount.userId !== link.userId) return redirectOnError("account_already_linked_to_different_user"); + const updateData = Object.fromEntries(Object.entries({ + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + idToken: tokens.idToken, + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") + }).filter(([_, value]) => value !== void 0)); + await ctx.context.internalAdapter.updateAccount(existingAccount.id, updateData); + } else if (!await ctx.context.internalAdapter.createAccount({ + userId: link.userId, + providerId: providerConfig.providerId, + accountId: userInfo.id, + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(","), + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + idToken: tokens.idToken + })) return redirectOnError("unable_to_link_account"); + let toRedirectTo$1; + try { + toRedirectTo$1 = callbackURL.toString(); + } catch { + toRedirectTo$1 = callbackURL; + } + throw ctx.redirect(toRedirectTo$1); + } + const result = await handleOAuthUserInfo(ctx, { + userInfo, + account: { + providerId: providerConfig.providerId, + accountId: userInfo.id, + ...tokens, + scope: tokens.scopes?.join(",") + }, + callbackURL, + disableSignUp: providerConfig.disableImplicitSignUp && !requestSignUp || providerConfig.disableSignUp, + overrideUserInfo: providerConfig.overrideUserInfo + }); + if (result.error) return redirectOnError(result.error.split(" ").join("_")); + const { session, user } = result.data; + await setSessionCookie(ctx, { + session, + user + }); + let toRedirectTo; + try { + toRedirectTo = (result.isRegister ? newUserURL || callbackURL : callbackURL).toString(); + } catch { + toRedirectTo = result.isRegister ? newUserURL || callbackURL : callbackURL; + } + throw ctx.redirect(toRedirectTo); +}); +const OAuth2LinkAccountBodySchema = z.object({ + providerId: z.string(), + callbackURL: z.string(), + scopes: z.array(z.string()).meta({ description: "Additional scopes to request when linking the account" }).optional(), + errorCallbackURL: z.string().meta({ description: "The URL to redirect to if there is an error during the link process" }).optional() +}); +const oAuth2LinkAccount = (options) => createAuthEndpoint("/oauth2/link", { + method: "POST", + body: OAuth2LinkAccountBodySchema, + use: [sessionMiddleware], + metadata: { openapi: { + description: "Link an OAuth2 account to the current user session", + responses: { "200": { + description: "Authorization URL generated successfully for linking an OAuth2 account", + content: { "application/json": { schema: { + type: "object", + properties: { + url: { + type: "string", + format: "uri", + description: "The authorization URL to redirect the user to for linking the OAuth2 account" + }, + redirect: { + type: "boolean", + description: "Indicates that the client should redirect to the provided URL", + enum: [true] + } + }, + required: ["url", "redirect"] + } } } + } } + } } +}, async (c) => { + const session = c.context.session; + if (!session) throw new APIError("UNAUTHORIZED", { message: GENERIC_OAUTH_ERROR_CODES.SESSION_REQUIRED }); + const provider = options.config.find((p) => p.providerId === c.body.providerId); + if (!provider) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.PROVIDER_NOT_FOUND }); + const { providerId, clientId, clientSecret, redirectURI, authorizationUrl, discoveryUrl, pkce, scopes, prompt, accessType, authorizationUrlParams } = provider; + let finalAuthUrl = authorizationUrl; + if (!finalAuthUrl) { + if (!discoveryUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + const discovery = await betterFetch(discoveryUrl, { + method: "GET", + headers: provider.discoveryHeaders, + onError(context) { + c.context.logger.error(context.error.message, context.error, { discoveryUrl }); + } + }); + if (discovery.data) finalAuthUrl = discovery.data.authorization_endpoint; + } + if (!finalAuthUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + const state = await generateState(c, { + userId: session.user.id, + email: session.user.email + }, void 0); + const additionalParams = typeof authorizationUrlParams === "function" ? authorizationUrlParams(c) : authorizationUrlParams; + const url = await createAuthorizationURL({ + id: providerId, + options: { + clientId, + clientSecret, + redirectURI: redirectURI || `${c.context.baseURL}/oauth2/callback/${providerId}` + }, + authorizationEndpoint: finalAuthUrl, + state: state.state, + codeVerifier: pkce ? state.codeVerifier : void 0, + scopes: c.body.scopes || scopes || [], + redirectURI: redirectURI || `${c.context.baseURL}/oauth2/callback/${providerId}`, + prompt, + accessType, + additionalParams + }); + return c.json({ + url: url.toString(), + redirect: true + }); +}); +async function getUserInfo(tokens, finalUserInfoUrl) { + if (tokens.idToken) { + const decoded = decodeJwt(tokens.idToken); + if (decoded) { + if (decoded.sub && decoded.email) return { + id: decoded.sub, + emailVerified: decoded.email_verified, + image: decoded.picture, + ...decoded + }; + } + } + if (!finalUserInfoUrl) return null; + const userInfo = await betterFetch(finalUserInfoUrl, { + method: "GET", + headers: { Authorization: `Bearer ${tokens.accessToken}` } + }); + return { + id: userInfo.data?.sub ?? "", + emailVerified: userInfo.data?.email_verified ?? false, + email: userInfo.data?.email, + image: userInfo.data?.picture, + name: userInfo.data?.name, + ...userInfo.data + }; +} +const genericOAuth = (options) => { + const seenIds = /* @__PURE__ */ new Set(); + const nonUniqueIds = /* @__PURE__ */ new Set(); + for (const config2 of options.config) { + const id = config2.providerId; + if (seenIds.has(id)) nonUniqueIds.add(id); + seenIds.add(id); + } + if (nonUniqueIds.size > 0) console.warn(`Duplicate provider IDs found: ${Array.from(nonUniqueIds).join(", ")}`); + return { + id: "generic-oauth", + init: (ctx) => { + return { context: { socialProviders: options.config.map((c) => { + let finalUserInfoUrl = c.userInfoUrl; + return { + id: c.providerId, + name: c.providerId, + async createAuthorizationURL(data) { + let finalAuthUrl = c.authorizationUrl; + if (!finalAuthUrl && c.discoveryUrl) { + const discovery = await betterFetch(c.discoveryUrl, { + method: "GET", + headers: c.discoveryHeaders + }); + if (discovery.data) { + finalAuthUrl = discovery.data.authorization_endpoint; + finalUserInfoUrl = finalUserInfoUrl ?? discovery.data.userinfo_endpoint; + } + } + if (!finalAuthUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + return createAuthorizationURL({ + id: c.providerId, + options: { + clientId: c.clientId, + clientSecret: c.clientSecret, + redirectURI: c.redirectURI + }, + authorizationEndpoint: finalAuthUrl, + state: data.state, + codeVerifier: c.pkce ? data.codeVerifier : void 0, + scopes: c.scopes || [], + redirectURI: `${ctx.baseURL}/oauth2/callback/${c.providerId}` + }); + }, + async validateAuthorizationCode(data) { + if (c.getToken) return c.getToken(data); + let finalTokenUrl = c.tokenUrl; + if (c.discoveryUrl) { + const discovery = await betterFetch(c.discoveryUrl, { + method: "GET", + headers: c.discoveryHeaders + }); + if (discovery.data) { + finalTokenUrl = discovery.data.token_endpoint; + finalUserInfoUrl = discovery.data.userinfo_endpoint; + } + } + if (!finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.TOKEN_URL_NOT_FOUND }); + return validateAuthorizationCode({ + headers: c.authorizationHeaders, + code: data.code, + codeVerifier: data.codeVerifier, + redirectURI: data.redirectURI, + options: { + clientId: c.clientId, + clientSecret: c.clientSecret, + redirectURI: c.redirectURI + }, + tokenEndpoint: finalTokenUrl, + authentication: c.authentication + }); + }, + async refreshAccessToken(refreshToken2) { + let finalTokenUrl = c.tokenUrl; + if (c.discoveryUrl) { + const discovery = await betterFetch(c.discoveryUrl, { + method: "GET", + headers: c.discoveryHeaders + }); + if (discovery.data) finalTokenUrl = discovery.data.token_endpoint; + } + if (!finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.TOKEN_URL_NOT_FOUND }); + return refreshAccessToken({ + refreshToken: refreshToken2, + options: { + clientId: c.clientId, + clientSecret: c.clientSecret + }, + authentication: c.authentication, + tokenEndpoint: finalTokenUrl + }); + }, + async getUserInfo(tokens) { + const userInfo = c.getUserInfo ? await c.getUserInfo(tokens) : await getUserInfo(tokens, finalUserInfoUrl); + if (!userInfo) return null; + const userMap = await c.mapProfileToUser?.(userInfo); + return { + user: { + id: userInfo?.id, + email: userInfo?.email, + emailVerified: userInfo?.emailVerified, + image: userInfo?.image, + name: userInfo?.name, + ...userMap + }, + data: userInfo + }; + }, + options: { overrideUserInfoOnSignIn: c.overrideUserInfo } + }; + }).concat(ctx.socialProviders) } }; + }, + endpoints: { + signInWithOAuth2: signInWithOAuth2(options), + oAuth2Callback: oAuth2Callback(options), + oAuth2LinkAccount: oAuth2LinkAccount(options) + }, + options, + $ERROR_CODES: GENERIC_OAUTH_ERROR_CODES + }; +}; +defineErrorCodes({ PASSWORD_COMPROMISED: "The password you entered has been compromised. Please choose a different password." }); +z.object({ + payload: z.record(z.string(), z.any()), + overrideOptions: z.record(z.string(), z.any()).optional() +}); +z.object({ + token: z.string(), + issuer: z.string().optional() +}); +z.object({ + email: z.email().meta({ description: "Email address to send the magic link" }), + name: z.string().meta({ description: 'User display name. Only used if the user is registering for the first time. Eg: "my-name"' }).optional(), + callbackURL: z.string().meta({ description: "URL to redirect after magic link verification" }).optional(), + newUserCallbackURL: z.string().meta({ description: "URL to redirect after new user signup. Only used if the user is registering for the first time." }).optional(), + errorCallbackURL: z.string().meta({ description: "URL to redirect after error." }).optional() +}); +z.object({ + token: z.string().meta({ description: "Verification token" }), + callbackURL: z.string().meta({ description: 'URL to redirect after magic link verification, if not provided the user will be redirected to the root URL. Eg: "/dashboard"' }).optional(), + errorCallbackURL: z.string().meta({ description: "URL to redirect after error." }).optional(), + newUserCallbackURL: z.string().meta({ description: "URL to redirect after new user signup. Only used if the user is registering for the first time." }).optional() +}); +z.object({ + clientId: z.string(), + clientSecret: z.string().optional(), + type: z.enum([ + "web", + "native", + "user-agent-based", + "public" + ]), + name: z.string(), + icon: z.string().optional(), + metadata: z.string().optional(), + disabled: z.boolean().optional().default(false), + redirectUrls: z.string(), + userId: z.string().optional(), + createdAt: z.date(), + updatedAt: z.date() +}); +z.object({ + accept: z.boolean(), + consent_code: z.string().optional().nullish() +}); +z.record(z.any(), z.any()); +z.object({ + redirect_uris: z.array(z.string()).meta({ description: 'A list of redirect URIs. Eg: ["https://client.example.com/callback"]' }), + token_endpoint_auth_method: z.enum([ + "none", + "client_secret_basic", + "client_secret_post" + ]).meta({ description: 'The authentication method for the token endpoint. Eg: "client_secret_basic"' }).default("client_secret_basic").optional(), + grant_types: z.array(z.enum([ + "authorization_code", + "implicit", + "password", + "client_credentials", + "refresh_token", + "urn:ietf:params:oauth:grant-type:jwt-bearer", + "urn:ietf:params:oauth:grant-type:saml2-bearer" + ])).meta({ description: 'The grant types supported by the application. Eg: ["authorization_code"]' }).default(["authorization_code"]).optional(), + response_types: z.array(z.enum(["code", "token"])).meta({ description: 'The response types supported by the application. Eg: ["code"]' }).default(["code"]).optional(), + client_name: z.string().meta({ description: 'The name of the application. Eg: "My App"' }).optional(), + client_uri: z.string().meta({ description: 'The URI of the application. Eg: "https://client.example.com"' }).optional(), + logo_uri: z.string().meta({ description: 'The URI of the application logo. Eg: "https://client.example.com/logo.png"' }).optional(), + scope: z.string().meta({ description: 'The scopes supported by the application. Separated by spaces. Eg: "profile email"' }).optional(), + contacts: z.array(z.string()).meta({ description: 'The contact information for the application. Eg: ["admin@example.com"]' }).optional(), + tos_uri: z.string().meta({ description: 'The URI of the application terms of service. Eg: "https://client.example.com/tos"' }).optional(), + policy_uri: z.string().meta({ description: 'The URI of the application privacy policy. Eg: "https://client.example.com/policy"' }).optional(), + jwks_uri: z.string().meta({ description: 'The URI of the application JWKS. Eg: "https://client.example.com/jwks"' }).optional(), + jwks: z.record(z.any(), z.any()).meta({ description: 'The JWKS of the application. Eg: {"keys": [{"kty": "RSA", "alg": "RS256", "use": "sig", "n": "...", "e": "..."}]}' }).optional(), + metadata: z.record(z.any(), z.any()).meta({ description: 'The metadata of the application. Eg: {"key": "value"}' }).optional(), + software_id: z.string().meta({ description: 'The software ID of the application. Eg: "my-software"' }).optional(), + software_version: z.string().meta({ description: 'The software version of the application. Eg: "1.0.0"' }).optional(), + software_statement: z.string().meta({ description: "The software statement of the application." }).optional() +}); +z.object({ + redirect_uris: z.array(z.string()), + token_endpoint_auth_method: z.enum([ + "none", + "client_secret_basic", + "client_secret_post" + ]).default("client_secret_basic").optional(), + grant_types: z.array(z.enum([ + "authorization_code", + "implicit", + "password", + "client_credentials", + "refresh_token", + "urn:ietf:params:oauth:grant-type:jwt-bearer", + "urn:ietf:params:oauth:grant-type:saml2-bearer" + ])).default(["authorization_code"]).optional(), + response_types: z.array(z.enum(["code", "token"])).default(["code"]).optional(), + client_name: z.string().optional(), + client_uri: z.string().optional(), + logo_uri: z.string().optional(), + scope: z.string().optional(), + contacts: z.array(z.string()).optional(), + tos_uri: z.string().optional(), + policy_uri: z.string().optional(), + jwks_uri: z.string().optional(), + jwks: z.record(z.string(), z.any()).optional(), + metadata: z.record(z.any(), z.any()).optional(), + software_id: z.string().optional(), + software_version: z.string().optional(), + software_statement: z.string().optional() +}); +z.record(z.any(), z.any()); +defineErrorCodes({ INVALID_SESSION_TOKEN: "Invalid session token" }); +z.object({ sessionToken: z.string().meta({ description: "The session token to set as active" }) }); +z.object({ sessionToken: z.string().meta({ description: "The session token to revoke" }) }); +z.object({ + callbackURL: z.string().meta({ description: "The URL to redirect to after the proxy" }), + cookies: z.string().meta({ description: "The cookies to set after the proxy" }) +}); +z.object({ idToken: z.string().meta({ description: "Google ID token, which the client obtains from the One Tap API" }) }); +z.object({ token: z.string().meta({ description: 'The token to verify. Eg: "some-token"' }) }); +createAuthMiddleware(async () => { + return {}; +}); +createAuthMiddleware({ use: [sessionMiddleware] }, async (ctx) => { + return { session: ctx.context.session }; +}); +defineErrorCodes({ + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION: "You are not allowed to create a new organization", + YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS: "You have reached the maximum number of organizations", + ORGANIZATION_ALREADY_EXISTS: "Organization already exists", + ORGANIZATION_SLUG_ALREADY_TAKEN: "Organization slug already taken", + ORGANIZATION_NOT_FOUND: "Organization not found", + USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION: "User is not a member of the organization", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION: "You are not allowed to update this organization", + YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION: "You are not allowed to delete this organization", + NO_ACTIVE_ORGANIZATION: "No active organization", + USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION: "User is already a member of this organization", + MEMBER_NOT_FOUND: "Member not found", + ROLE_NOT_FOUND: "Role not found", + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM: "You are not allowed to create a new team", + TEAM_ALREADY_EXISTS: "Team already exists", + TEAM_NOT_FOUND: "Team not found", + YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER: "You cannot leave the organization as the only owner", + YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER: "You cannot leave the organization without an owner", + YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER: "You are not allowed to delete this member", + YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION: "You are not allowed to invite users to this organization", + USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION: "User is already invited to this organization", + INVITATION_NOT_FOUND: "Invitation not found", + YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION: "You are not the recipient of the invitation", + EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION: "Email verification required before accepting or rejecting invitation", + YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION: "You are not allowed to cancel this invitation", + INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION: "Inviter is no longer a member of the organization", + YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE: "You are not allowed to invite a user with this role", + FAILED_TO_RETRIEVE_INVITATION: "Failed to retrieve invitation", + YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS: "You have reached the maximum number of teams", + UNABLE_TO_REMOVE_LAST_TEAM: "Unable to remove last team", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER: "You are not allowed to update this member", + ORGANIZATION_MEMBERSHIP_LIMIT_REACHED: "Organization membership limit reached", + YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION: "You are not allowed to create teams in this organization", + YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION: "You are not allowed to delete teams in this organization", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM: "You are not allowed to update this team", + YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM: "You are not allowed to delete this team", + INVITATION_LIMIT_REACHED: "Invitation limit reached", + TEAM_MEMBER_LIMIT_REACHED: "Team member limit reached", + USER_IS_NOT_A_MEMBER_OF_THE_TEAM: "User is not a member of the team", + YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM: "You are not allowed to list the members of this team", + YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM: "You do not have an active team", + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER: "You are not allowed to create a new member", + YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER: "You are not allowed to remove a team member", + YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION: "You are not allowed to access this organization as an owner", + YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION: "You are not a member of this organization", + MISSING_AC_INSTANCE: "Dynamic Access Control requires a pre-defined ac instance on the server auth plugin. Read server logs for more information", + YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE: "You must be in an organization to create a role", + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE: "You are not allowed to create a role", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE: "You are not allowed to update a role", + YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE: "You are not allowed to delete a role", + YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE: "You are not allowed to read a role", + YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE: "You are not allowed to list a role", + YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE: "You are not allowed to get a role", + TOO_MANY_ROLES: "This organization has too many roles", + INVALID_RESOURCE: "The provided permission includes an invalid resource", + ROLE_NAME_IS_ALREADY_TAKEN: "That role name is already taken", + CANNOT_DELETE_A_PRE_DEFINED_ROLE: "Cannot delete a pre-defined role", + ROLE_IS_ASSIGNED_TO_MEMBERS: "Cannot delete a role that is assigned to members. Please reassign the members to a different role first" +}); +z.object({ + organizationId: z.string().optional().meta({ description: "The id of the organization to create the role in. If not provided, the user's active organization will be used." }), + role: z.string().meta({ description: "The name of the role to create" }), + permission: z.record(z.string(), z.array(z.string())).meta({ description: "The permission to assign to the role" }) +}); +z.object({ organizationId: z.string().optional().meta({ description: "The id of the organization to create the role in. If not provided, the user's active organization will be used." }) }).and(z.union([z.object({ roleName: z.string().nonempty().meta({ description: "The name of the role to delete" }) }), z.object({ roleId: z.string().nonempty().meta({ description: "The id of the role to delete" }) })])); +z.object({ organizationId: z.string().optional().meta({ description: "The id of the organization to list roles for. If not provided, the user's active organization will be used." }) }).optional(); +z.object({ organizationId: z.string().optional().meta({ description: "The id of the organization to read a role for. If not provided, the user's active organization will be used." }) }).and(z.union([z.object({ roleName: z.string().nonempty().meta({ description: "The name of the role to read" }) }), z.object({ roleId: z.string().nonempty().meta({ description: "The id of the role to read" }) })])).optional(); +z.union([z.object({ roleName: z.string().nonempty().meta({ description: "The name of the role to update" }) }), z.object({ roleId: z.string().nonempty().meta({ description: "The id of the role to update" }) })]); +z.object({ + email: z.string().meta({ description: "The email address of the user to invite" }), + role: z.union([z.string().meta({ description: "The role to assign to the user" }), z.array(z.string().meta({ description: "The roles to assign to the user" }))]).meta({ description: 'The role(s) to assign to the user. It can be `admin`, `member`, owner. Eg: "member"' }), + organizationId: z.string().meta({ description: "The organization ID to invite the user to" }).optional(), + resend: z.boolean().meta({ description: "Resend the invitation email, if the user is already invited. Eg: true" }).optional(), + teamId: z.union([z.string().meta({ description: "The team ID to invite the user to" }).optional(), z.array(z.string()).meta({ description: "The team IDs to invite the user to" }).optional()]) +}); +z.object({ invitationId: z.string().meta({ description: "The ID of the invitation to accept" }) }); +z.object({ invitationId: z.string().meta({ description: "The ID of the invitation to reject" }) }); +z.object({ invitationId: z.string().meta({ description: "The ID of the invitation to cancel" }) }); +z.object({ id: z.string().meta({ description: "The ID of the invitation to get" }) }); +z.object({ organizationId: z.string().meta({ description: "The ID of the organization to list invitations for" }).optional() }).optional(); +z.object({ + userId: z.coerce.string().meta({ description: 'The user Id which represents the user to be added as a member. If `null` is provided, then it\'s expected to provide session headers. Eg: "user-id"' }), + role: z.union([z.string(), z.array(z.string())]).meta({ description: 'The role(s) to assign to the new member. Eg: ["admin", "sale"]' }), + organizationId: z.string().meta({ description: `An optional organization ID to pass. If not provided, will default to the user's active organization. Eg: "org-id"` }).optional(), + teamId: z.string().meta({ description: 'An optional team ID to add the member to. Eg: "team-id"' }).optional() +}); +z.object({ + memberIdOrEmail: z.string().meta({ description: "The ID or email of the member to remove" }), + organizationId: z.string().meta({ description: 'The ID of the organization to remove the member from. If not provided, the active organization will be used. Eg: "org-id"' }).optional() +}); +z.object({ + role: z.union([z.string(), z.array(z.string())]).meta({ description: 'The new role to be applied. This can be a string or array of strings representing the roles. Eg: ["admin", "sale"]' }), + memberId: z.string().meta({ description: 'The member id to apply the role update to. Eg: "member-id"' }), + organizationId: z.string().meta({ description: 'An optional organization ID which the member is a part of to apply the role update. If not provided, you must provide session headers to get the active organization. Eg: "organization-id"' }).optional() +}); +z.object({ organizationId: z.string().meta({ description: 'The organization Id for the member to leave. Eg: "organization-id"' }) }); +z.object({ + userId: z.string().meta({ description: "The user ID to get the role for. If not provided, will default to the current user's" }).optional(), + organizationId: z.string().meta({ description: `The organization ID to list members for. If not provided, will default to the user's active organization. Eg: "organization-id"` }).optional(), + organizationSlug: z.string().meta({ description: `The organization slug to list members for. If not provided, will default to the user's active organization. Eg: "organization-slug"` }).optional() +}).optional(); +z.object({ + name: z.string().min(1).meta({ description: "The name of the organization" }), + slug: z.string().min(1).meta({ description: "The slug of the organization" }), + userId: z.coerce.string().meta({ description: 'The user id of the organization creator. If not provided, the current user will be used. Should only be used by admins or when called by the server. server-only. Eg: "user-id"' }).optional(), + logo: z.string().meta({ description: "The logo of the organization" }).optional(), + metadata: z.record(z.string(), z.any()).meta({ description: "The metadata of the organization" }).optional(), + keepCurrentActiveOrganization: z.boolean().meta({ description: "Whether to keep the current active organization active after creating a new one. Eg: true" }).optional() +}); +z.object({ slug: z.string().meta({ description: 'The organization slug to check. Eg: "my-org"' }) }); +z.object({ + name: z.string().min(1).meta({ description: "The name of the organization" }).optional(), + slug: z.string().min(1).meta({ description: "The slug of the organization" }).optional(), + logo: z.string().meta({ description: "The logo of the organization" }).optional(), + metadata: z.record(z.string(), z.any()).meta({ description: "The metadata of the organization" }).optional() +}); +z.object({ organizationId: z.string().meta({ description: "The organization id to delete" }) }); +z.optional(z.object({ + organizationId: z.string().meta({ description: "The organization id to get" }).optional(), + organizationSlug: z.string().meta({ description: "The organization slug to get" }).optional(), + membersLimit: z.number().or(z.string().transform((val) => parseInt(val))).meta({ description: "The limit of members to get. By default, it uses the membershipLimit option which defaults to 100." }).optional() +})); +z.object({ + organizationId: z.string().meta({ description: 'The organization id to set as active. It can be null to unset the active organization. Eg: "org-id"' }).nullable().optional(), + organizationSlug: z.string().meta({ description: 'The organization slug to set as active. It can be null to unset the active organization if organizationId is not provided. Eg: "org-slug"' }).optional() +}); +const roleSchema = z.string(); +const invitationStatus = z.enum([ + "pending", + "accepted", + "rejected", + "canceled" +]).default("pending"); +z.object({ + id: z.string().default(generateId), + name: z.string(), + slug: z.string(), + logo: z.string().nullish().optional(), + metadata: z.record(z.string(), z.unknown()).or(z.string().transform((v) => JSON.parse(v))).optional(), + createdAt: z.date() +}); +z.object({ + id: z.string().default(generateId), + organizationId: z.string(), + userId: z.coerce.string(), + role: roleSchema, + createdAt: z.date().default(() => /* @__PURE__ */ new Date()) +}); +z.object({ + id: z.string().default(generateId), + organizationId: z.string(), + email: z.string(), + role: roleSchema, + status: invitationStatus, + teamId: z.string().nullish(), + inviterId: z.string(), + expiresAt: z.date(), + createdAt: z.date().default(() => /* @__PURE__ */ new Date()) +}); +z.object({ + id: z.string().default(generateId), + name: z.string().min(1), + organizationId: z.string(), + createdAt: z.date(), + updatedAt: z.date().optional() +}); +z.object({ + id: z.string().default(generateId), + teamId: z.string(), + userId: z.string(), + createdAt: z.date().default(() => /* @__PURE__ */ new Date()) +}); +z.object({ + id: z.string().default(generateId), + organizationId: z.string(), + role: z.string(), + permission: z.record(z.string(), z.array(z.string())), + createdAt: z.date().default(() => /* @__PURE__ */ new Date()), + updatedAt: z.date().optional() +}); +const defaultRoles = [ + "admin", + "member", + "owner" +]; +z.union([z.enum(defaultRoles), z.array(z.enum(defaultRoles))]); +z.object({ + name: z.string().meta({ description: 'The name of the team. Eg: "my-team"' }), + organizationId: z.string().meta({ description: 'The organization ID which the team will be created in. Defaults to the active organization. Eg: "organization-id"' }).optional() +}); +z.object({ + teamId: z.string().meta({ description: `The team ID of the team to remove. Eg: "team-id"` }), + organizationId: z.string().meta({ description: `The organization ID which the team falls under. If not provided, it will default to the user's active organization. Eg: "organization-id"` }).optional() +}); +z.optional(z.object({ organizationId: z.string().meta({ description: `The organization ID which the teams are under to list. Defaults to the users active organization. Eg: "organization-id"` }).optional() })); +z.object({ teamId: z.string().meta({ description: "The team id to set as active. It can be null to unset the active team" }).nullable().optional() }); +z.optional(z.object({ teamId: z.string().optional().meta({ description: "The team whose members we should return. If this is not provided the members of the current active team get returned." }) })); +z.object({ + teamId: z.string().meta({ description: "The team the user should be a member of." }), + userId: z.coerce.string().meta({ description: "The user Id which represents the user to be added as a member." }) +}); +z.object({ + teamId: z.string().meta({ description: "The team the user should be removed from." }), + userId: z.coerce.string().meta({ description: "The user which should be removed from the team." }) +}); +z.object({ organizationId: z.string().optional() }).and(z.union([z.object({ + permission: z.record(z.string(), z.array(z.string())), + permissions: z.undefined() +}), z.object({ + permission: z.undefined(), + permissions: z.record(z.string(), z.array(z.string())) +})])); +defineErrorCodes({ + INVALID_PHONE_NUMBER: "Invalid phone number", + PHONE_NUMBER_EXIST: "Phone number already exists", + PHONE_NUMBER_NOT_EXIST: "phone number isn't registered", + INVALID_PHONE_NUMBER_OR_PASSWORD: "Invalid phone number or password", + UNEXPECTED_ERROR: "Unexpected error", + OTP_NOT_FOUND: "OTP not found", + OTP_EXPIRED: "OTP expired", + INVALID_OTP: "Invalid OTP", + PHONE_NUMBER_NOT_VERIFIED: "Phone number not verified", + PHONE_NUMBER_CANNOT_BE_UPDATED: "Phone number cannot be updated", + SEND_OTP_NOT_IMPLEMENTED: "sendOTP not implemented", + TOO_MANY_ATTEMPTS: "Too many attempts" +}); +z.object({ + phoneNumber: z.string().meta({ description: 'Phone number to sign in. Eg: "+1234567890"' }), + password: z.string().meta({ description: "Password to use for sign in." }), + rememberMe: z.boolean().meta({ description: "Remember the session. Eg: true" }).optional() +}); +z.object({ phoneNumber: z.string().meta({ description: 'Phone number to send OTP. Eg: "+1234567890"' }) }); +z.object({ + phoneNumber: z.string().meta({ description: 'Phone number to verify. Eg: "+1234567890"' }), + code: z.string().meta({ description: 'OTP code. Eg: "123456"' }), + disableSession: z.boolean().meta({ description: "Disable session creation after verification. Eg: false" }).optional(), + updatePhoneNumber: z.boolean().meta({ description: "Check if there is a session and update the phone number. Eg: true" }).optional() +}).and(z.record(z.string(), z.any())); +z.object({ phoneNumber: z.string() }); +z.object({ + otp: z.string().meta({ description: 'The one time password to reset the password. Eg: "123456"' }), + phoneNumber: z.string().meta({ description: 'The phone number to the account which intends to reset the password for. Eg: "+1234567890"' }), + newPassword: z.string().meta({ description: `The new password. Eg: "new-and-secure-password"` }) +}); +z.object({ + walletAddress: z.string().regex(/^0[xX][a-fA-F0-9]{40}$/i).length(42), + chainId: z.number().int().positive().max(2147483647).optional().default(1) +}); +defineErrorCodes({ + OTP_NOT_ENABLED: "OTP not enabled", + OTP_HAS_EXPIRED: "OTP has expired", + TOTP_NOT_ENABLED: "TOTP not enabled", + TWO_FACTOR_NOT_ENABLED: "Two factor isn't enabled", + BACKUP_CODES_NOT_ENABLED: "Backup codes aren't enabled", + INVALID_BACKUP_CODE: "Invalid backup code", + INVALID_CODE: "Invalid code", + TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: "Too many attempts. Please request a new code.", + INVALID_TWO_FACTOR_COOKIE: "Invalid two factor cookie" +}); +z.object({ + code: z.string().meta({ description: `A backup code to verify. Eg: "123456"` }), + disableSession: z.boolean().meta({ description: "If true, the session cookie will not be set." }).optional(), + trustDevice: z.boolean().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional() +}); +z.object({ userId: z.coerce.string().meta({ description: `The user ID to view all backup codes. Eg: "user-id"` }) }); +z.object({ password: z.string().meta({ description: "The users password." }) }); +z.object({ + code: z.string().meta({ description: 'The otp code to verify. Eg: "012345"' }), + trustDevice: z.boolean().optional().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }) +}); +z.object({ trustDevice: z.boolean().optional().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }) }).optional(); +z.object({ secret: z.string().meta({ description: "The secret to generate the TOTP code" }) }); +z.object({ password: z.string().meta({ description: "User password" }) }); +z.object({ + code: z.string().meta({ description: 'The otp code to verify. Eg: "012345"' }), + trustDevice: z.boolean().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional() +}); +z.object({ + password: z.string().meta({ description: "User password" }), + issuer: z.string().meta({ description: "Custom issuer for the TOTP URI" }).optional() +}); +z.object({ password: z.string().meta({ description: "User password" }) }); +defineErrorCodes({ + INVALID_USERNAME_OR_PASSWORD: "Invalid username or password", + EMAIL_NOT_VERIFIED: "Email not verified", + UNEXPECTED_ERROR: "Unexpected error", + USERNAME_IS_ALREADY_TAKEN: "Username is already taken. Please try another.", + USERNAME_TOO_SHORT: "Username is too short", + USERNAME_TOO_LONG: "Username is too long", + INVALID_USERNAME: "Username is invalid", + INVALID_DISPLAY_USERNAME: "Display username is invalid" +}); +z.object({ + username: z.string().meta({ description: "The username of the user" }), + password: z.string().meta({ description: "The password of the user" }), + rememberMe: z.boolean().meta({ description: "Remember the user session" }).optional(), + callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional() +}); +z.object({ username: z.string().meta({ description: "The username to check" }) }); +const auth = betterAuth({ + baseURL: private_env.ORIGIN, + secret: private_env.BETTER_AUTH_SECRET, + database: drizzleAdapter(db, { provider: "pg" }), + emailAndPassword: { enabled: true }, + databaseHooks: { + user: { + create: { + after: async (user) => { + const [personalTeam] = await db.insert(team).values({ + name: user.name, + isPersonal: true, + ownerId: user.id, + avatarUrl: user.image + }).returning(); + await db.insert(teamMember).values({ + teamId: personalTeam.id, + userId: user.id, + role: "owner" + }); + } + } + } + }, + plugins: [ + genericOAuth({ + config: [ + { + providerId: "osu", + clientId: private_env.OSU_CLIENT_ID, + clientSecret: private_env.OSU_CLIENT_SECRET, + authorizationUrl: "https://osu.ppy.sh/oauth/authorize", + tokenUrl: "https://osu.ppy.sh/oauth/token", + userInfoUrl: "https://osu.ppy.sh/api/v2/me", + scopes: ["identify", "public"], + pkce: false, + authentication: "post", + // ── FIX: Use ORIGIN env var instead of hardcoded localhost ── + redirectURI: `${private_env.ORIGIN}/api/auth/oauth2/callback/osu`, + getToken: async ({ code, redirectURI }) => { + const res = await fetch("https://osu.ppy.sh/oauth/token", { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + client_id: private_env.OSU_CLIENT_ID, + client_secret: private_env.OSU_CLIENT_SECRET, + code, + grant_type: "authorization_code", + redirect_uri: redirectURI + }) + }); + const data = await res.json(); + if (!res.ok) { + console.error("osu! token exchange failed:", data); + throw new Error("Token exchange failed"); + } + return { + accessToken: data.access_token, + refreshToken: data.refresh_token, + accessTokenExpiresAt: new Date(Date.now() + data.expires_in * 1e3), + scopes: ["identify", "public"], + raw: data + }; + }, + mapProfileToUser(profile) { + return { + name: profile.username, + image: profile.avatar_url, + email: `${profile.id}@osu.local` + }; + } + } + ] + }), + sveltekitCookies(getRequestEvent) + // must be last + ] +}); +export { + auth as a, + createKyselyAdapter as c, + getKyselyDatabaseType as g, + svelteKitHandler as s +}; diff --git a/.svelte-kit/adapter-node/chunks/bun-sqlite-dialect.js b/.svelte-kit/adapter-node/chunks/bun-sqlite-dialect.js new file mode 100644 index 0000000..6fe94c6 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/bun-sqlite-dialect.js @@ -0,0 +1,155 @@ +import { CompiledQuery, DefaultQueryCompiler, DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE, sql } from "kysely"; +var BunSqliteAdapter = class { + get supportsCreateIfNotExists() { + return true; + } + get supportsTransactionalDdl() { + return false; + } + get supportsReturning() { + return true; + } + async acquireMigrationLock() { + } + async releaseMigrationLock() { + } + get supportsOutput() { + return true; + } +}; +var BunSqliteDriver = class { + #config; + #connectionMutex = new ConnectionMutex(); + #db; + #connection; + constructor(config) { + this.#config = { ...config }; + } + async init() { + this.#db = this.#config.database; + this.#connection = new BunSqliteConnection(this.#db); + if (this.#config.onCreateConnection) await this.#config.onCreateConnection(this.#connection); + } + async acquireConnection() { + await this.#connectionMutex.lock(); + return this.#connection; + } + async beginTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("begin")); + } + async commitTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("commit")); + } + async rollbackTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("rollback")); + } + async releaseConnection() { + this.#connectionMutex.unlock(); + } + async destroy() { + this.#db?.close(); + } +}; +var BunSqliteConnection = class { + #db; + constructor(db) { + this.#db = db; + } + executeQuery(compiledQuery) { + const { sql: sql$1, parameters } = compiledQuery; + const stmt = this.#db.prepare(sql$1); + return Promise.resolve({ rows: stmt.all(parameters) }); + } + async *streamQuery() { + throw new Error("Streaming query is not supported by SQLite driver."); + } +}; +var ConnectionMutex = class { + #promise; + #resolve; + async lock() { + while (await this.#promise) await this.#promise; + this.#promise = new Promise((resolve) => { + this.#resolve = resolve; + }); + } + unlock() { + const resolve = this.#resolve; + this.#promise = void 0; + this.#resolve = void 0; + resolve?.(); + } +}; +var BunSqliteIntrospector = class { + #db; + constructor(db) { + this.#db = db; + } + async getSchemas() { + return []; + } + async getTables(options = { withInternalKyselyTables: false }) { + let query = this.#db.selectFrom("sqlite_schema").where("type", "=", "table").where("name", "not like", "sqlite_%").select("name").$castTo(); + if (!options.withInternalKyselyTables) query = query.where("name", "!=", DEFAULT_MIGRATION_TABLE).where("name", "!=", DEFAULT_MIGRATION_LOCK_TABLE); + const tables = await query.execute(); + return Promise.all(tables.map(({ name }) => this.#getTableMetadata(name))); + } + async getMetadata(options) { + return { tables: await this.getTables(options) }; + } + async #getTableMetadata(table) { + const db = this.#db; + const autoIncrementCol = (await db.selectFrom("sqlite_master").where("name", "=", table).select("sql").$castTo().execute())[0]?.sql?.split(/[\(\),]/)?.find((it) => it.toLowerCase().includes("autoincrement"))?.split(/\s+/)?.[0]?.replace(/["`]/g, ""); + return { + name: table, + columns: (await db.selectFrom(sql`pragma_table_info(${table})`.as("table_info")).select([ + "name", + "type", + "notnull", + "dflt_value" + ]).execute()).map((col) => ({ + name: col.name, + dataType: col.type, + isNullable: !col.notnull, + isAutoIncrementing: col.name === autoIncrementCol, + hasDefaultValue: col.dflt_value != null + })), + isView: true + }; + } +}; +var BunSqliteQueryCompiler = class extends DefaultQueryCompiler { + getCurrentParameterPlaceholder() { + return "?"; + } + getLeftIdentifierWrapper() { + return '"'; + } + getRightIdentifierWrapper() { + return '"'; + } + getAutoIncrement() { + return "autoincrement"; + } +}; +var BunSqliteDialect = class { + #config; + constructor(config) { + this.#config = { ...config }; + } + createDriver() { + return new BunSqliteDriver(this.#config); + } + createQueryCompiler() { + return new BunSqliteQueryCompiler(); + } + createAdapter() { + return new BunSqliteAdapter(); + } + createIntrospector(db) { + return new BunSqliteIntrospector(db); + } +}; +export { + BunSqliteDialect +}; diff --git a/.svelte-kit/adapter-node/chunks/engine.js b/.svelte-kit/adapter-node/chunks/engine.js new file mode 100644 index 0000000..f6bafe4 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/engine.js @@ -0,0 +1,350 @@ +import { d as db, b as match, e as matchParticipant, t as teamMember, f as matchParticipantPlayer, g as matchGame, h as matchGameScore, i as matchQueue, p as playerRating } from "./index2.js"; +import { eq, sql, asc } from "drizzle-orm"; +const MATCH_STATES = { + CREATED: "CREATED", + LOBBY: "LOBBY", + ROLLING: "ROLLING", + PICKING: "PICKING", + PLAYING: "PLAYING", + FINISHED: "FINISHED", + CANCELLED: "CANCELLED" +}; +const GAME_STATES = { + PLAYING: "PLAYING", + FINISHED: "FINISHED" +}; +async function joinQueue(userId, teamId) { + let rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, userId) + }); + if (!rating) { + [rating] = await db.insert(playerRating).values({ userId, elo: 1e3 }).returning(); + } + const existing = await db.query.matchQueue.findFirst({ + where: eq(matchQueue.userId, userId) + }); + if (existing) throw new Error("Already in queue"); + await db.insert(matchQueue).values({ + userId, + teamId, + elo: rating.elo + }); + return tryMatchFromQueue(); +} +async function leaveQueue(userId) { + await db.delete(matchQueue).where(eq(matchQueue.userId, userId)); +} +async function getQueueStatus(userId) { + const entry = await db.query.matchQueue.findFirst({ + where: eq(matchQueue.userId, userId) + }); + const count = await db.select({ count: sql`count(*)` }).from(matchQueue); + return { + inQueue: !!entry, + queueSize: Number(count[0].count), + joinedAt: entry?.joinedAt ?? null + }; +} +async function tryMatchFromQueue() { + const queue = await db.query.matchQueue.findMany({ + orderBy: asc(matchQueue.elo) + }); + if (queue.length < 2) return null; + let bestPair = null; + let smallestGap = Infinity; + for (let i = 0; i < queue.length - 1; i++) { + const gap = Math.abs(queue[i].elo - queue[i + 1].elo); + if (gap < smallestGap) { + smallestGap = gap; + bestPair = [queue[i], queue[i + 1]]; + } + } + if (!bestPair) return null; + const avgElo = (bestPair[0].elo + bestPair[1].elo) / 2; + const selectedPool = await selectMappoolForRating(avgElo); + if (!selectedPool) throw new Error("No mappools available"); + await db.delete(matchQueue).where(eq(matchQueue.userId, bestPair[0].userId)); + await db.delete(matchQueue).where(eq(matchQueue.userId, bestPair[1].userId)); + const created = await createMatch({ + name: "Ranked Match", + config: { bestOf: 7, teamSize: 1, scoringType: "score" }, + mappoolId: selectedPool.id, + teams: [bestPair[0].teamId, bestPair[1].teamId], + createdBy: "system" + }); + return created; +} +async function selectMappoolForRating(avgElo) { + const pools = await db.query.mappool.findMany({ + with: { slots: true } + }); + if (pools.length === 0) return null; + const targetStars = 3 + (avgElo - 800) / 400 * 4; + const clampedTarget = Math.max(2, Math.min(8, targetStars)); + let bestPool = pools[0]; + let bestDiff = Infinity; + for (const pool of pools) { + if (pool.slots.length === 0) continue; + const avgStars = pool.slots.reduce((sum, s) => sum + (s.starRating ?? 0), 0) / pool.slots.length; + const diff = Math.abs(avgStars - clampedTarget); + if (diff < bestDiff) { + bestDiff = diff; + bestPool = pool; + } + } + return bestPool; +} +async function createMatch(opts) { + const { name, config, mappoolId, teams, createdBy } = opts; + const [created] = await db.insert(match).values({ + name, + config, + mappoolId, + createdBy, + state: MATCH_STATES.CREATED + }).returning(); + for (let i = 0; i < teams.length; i++) { + const [participant] = await db.insert(matchParticipant).values({ + matchId: created.id, + teamId: teams[i], + slot: i + 1 + }).returning(); + const members = await db.query.teamMember.findMany({ + where: eq(teamMember.teamId, teams[i]) + }); + for (const member of members) { + await db.insert(matchParticipantPlayer).values({ + participantId: participant.id, + userId: member.userId + }); + } + } + return getMatchFull(created.id); +} +async function moveToLobby(matchId, osuLobbyId) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.CREATED); + await db.update(match).set({ + state: MATCH_STATES.LOBBY, + osuLobbyId: osuLobbyId ?? null, + startedAt: /* @__PURE__ */ new Date() + }).where(eq(match.id, matchId)); + return getMatchFull(matchId); +} +async function moveToRolling(matchId) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.LOBBY); + await db.update(match).set({ state: MATCH_STATES.ROLLING }).where(eq(match.id, matchId)); + return getMatchFull(matchId); +} +async function submitRoll(matchId, participantId, value) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.ROLLING); + await db.update(matchParticipant).set({ rollValue: value }).where(eq(matchParticipant.id, participantId)); + const participants = await db.query.matchParticipant.findMany({ + where: eq(matchParticipant.matchId, matchId) + }); + const allRolled = participants.every( + (p) => p.id === participantId ? true : p.rollValue !== null + ); + if (allRolled) { + const sorted = [...participants].map((p) => ({ + ...p, + rollValue: p.id === participantId ? value : p.rollValue + })).sort((a, b) => b.rollValue - a.rollValue); + for (let i = 0; i < sorted.length; i++) { + await db.update(matchParticipant).set({ pickOrder: i + 1 }).where(eq(matchParticipant.id, sorted[i].id)); + } + await db.update(match).set({ state: MATCH_STATES.PICKING }).where(eq(match.id, matchId)); + } + return getMatchFull(matchId); +} +async function pickMap(matchId, participantId, mappoolSlotId) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.PICKING); + const participants = await db.query.matchParticipant.findMany({ + where: eq(matchParticipant.matchId, matchId) + }); + const totalGames = await db.query.matchGame.findMany({ + where: eq(matchGame.matchId, matchId) + }); + const expectedPicker = getExpectedPicker(participants, totalGames.length); + if (expectedPicker.id !== participantId) { + throw new Error("Not your turn to pick"); + } + const alreadyPlayed = totalGames.some((g) => g.mappoolSlotId === mappoolSlotId); + if (alreadyPlayed) { + throw new Error("This map has already been played"); + } + const [game] = await db.insert(matchGame).values({ + matchId, + gameNumber: totalGames.length + 1, + mappoolSlotId, + pickedByParticipantId: participantId, + state: GAME_STATES.PLAYING, + startedAt: /* @__PURE__ */ new Date() + }).returning(); + await db.update(match).set({ state: MATCH_STATES.PLAYING }).where(eq(match.id, matchId)); + return game; +} +async function submitGameScores(matchGameId, scores) { + const game = await db.query.matchGame.findFirst({ + where: eq(matchGame.id, matchGameId) + }); + if (!game) throw new Error("Game not found"); + if (game.state !== GAME_STATES.PLAYING) throw new Error(`Game is ${game.state}, not PLAYING`); + for (const s of scores) { + await db.insert(matchGameScore).values({ + matchGameId, + playerId: s.playerId, + score: s.score, + accuracy: s.accuracy ?? 0, + maxCombo: s.maxCombo ?? 0, + count300: s.count300 ?? 0, + count100: s.count100 ?? 0, + count50: s.count50 ?? 0, + countMiss: s.countMiss ?? 0, + mods: s.mods ?? [], + passed: s.passed ?? true + }); + } + const allScores = await db.query.matchGameScore.findMany({ + where: eq(matchGameScore.matchGameId, matchGameId), + with: { player: true } + }); + const participantScores = /* @__PURE__ */ new Map(); + for (const s of allScores) { + const pid = s.player.participantId; + participantScores.set(pid, (participantScores.get(pid) ?? 0) + s.score); + } + let winnerId = null; + let highScore = -1; + for (const [pid, total] of participantScores) { + if (total > highScore) { + highScore = total; + winnerId = pid; + } + } + await db.update(matchGame).set({ + state: GAME_STATES.FINISHED, + winnerParticipantId: winnerId, + finishedAt: /* @__PURE__ */ new Date() + }).where(eq(matchGame.id, matchGameId)); + if (winnerId) { + const participant = await db.query.matchParticipant.findFirst({ + where: eq(matchParticipant.id, winnerId) + }); + if (participant) { + await db.update(matchParticipant).set({ score: participant.score + 1 }).where(eq(matchParticipant.id, winnerId)); + } + } + const m = await getMatchOrThrow(game.matchId); + const config = m.config; + const winsNeeded = Math.ceil(config.bestOf / 2); + const participants = await db.query.matchParticipant.findMany({ + where: eq(matchParticipant.matchId, game.matchId) + }); + const matchWinner = participants.find((p) => p.score >= winsNeeded); + if (matchWinner) { + await db.update(match).set({ + state: MATCH_STATES.FINISHED, + winnerId: matchWinner.teamId, + finishedAt: /* @__PURE__ */ new Date() + }).where(eq(match.id, game.matchId)); + await updateElo(participants, matchWinner.id); + } else { + await db.update(match).set({ state: MATCH_STATES.PICKING }).where(eq(match.id, game.matchId)); + } + return getMatchFull(game.matchId); +} +async function updateElo(participants, winnerId) { + for (const p of participants) { + const players = await db.query.matchParticipantPlayer.findMany({ + where: eq(matchParticipantPlayer.participantId, p.id) + }); + const isWinner = p.id === winnerId; + for (const player of players) { + let rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, player.userId) + }); + if (!rating) { + [rating] = await db.insert(playerRating).values({ userId: player.userId, elo: 1e3 }).returning(); + } + const K = 32; + const expected = 0.5; + const actual = isWinner ? 1 : 0; + const newElo = Math.round(rating.elo + K * (actual - expected)); + await db.update(playerRating).set({ + elo: newElo, + wins: isWinner ? rating.wins + 1 : rating.wins, + losses: isWinner ? rating.losses : rating.losses + 1, + updatedAt: /* @__PURE__ */ new Date() + }).where(eq(playerRating.userId, player.userId)); + } + } +} +async function cancelMatch(matchId) { + await db.update(match).set({ + state: MATCH_STATES.CANCELLED, + finishedAt: /* @__PURE__ */ new Date() + }).where(eq(match.id, matchId)); + return getMatchFull(matchId); +} +function getExpectedPicker(participants, gameCount) { + const sorted = [...participants].sort((a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99)); + const pickerIndex = gameCount % sorted.length; + return sorted[pickerIndex]; +} +async function getMatchFull(matchId) { + const m = await db.query.match.findFirst({ + where: eq(match.id, matchId), + with: { + mappool: { + with: { slots: true } + }, + participants: { + with: { + team: true, + players: true + } + }, + games: { + with: { + slot: true, + scores: { + with: { player: true } + } + }, + orderBy: (g, { asc: asc2 }) => [asc2(g.gameNumber)] + } + } + }); + if (!m) throw new Error("Match not found"); + return m; +} +async function getMatchOrThrow(matchId) { + const m = await db.query.match.findFirst({ + where: eq(match.id, matchId) + }); + if (!m) throw new Error("Match not found"); + return m; +} +function assertState(current, expected) { + if (current !== expected) { + throw new Error(`Match is ${current}, expected ${expected}`); + } +} +export { + MATCH_STATES as M, + cancelMatch as a, + moveToRolling as b, + createMatch as c, + submitGameScores as d, + getQueueStatus as e, + getMatchFull as g, + joinQueue as j, + leaveQueue as l, + moveToLobby as m, + pickMap as p, + submitRoll as s +}; diff --git a/.svelte-kit/adapter-node/chunks/environment.js b/.svelte-kit/adapter-node/chunks/environment.js new file mode 100644 index 0000000..d81356b --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/environment.js @@ -0,0 +1,14 @@ +let building = false; +let prerendering = false; +function set_building() { + building = true; +} +function set_prerendering() { + prerendering = true; +} +export { + set_prerendering as a, + building as b, + prerendering as p, + set_building as s +}; diff --git a/.svelte-kit/adapter-node/chunks/exports.js b/.svelte-kit/adapter-node/chunks/exports.js new file mode 100644 index 0000000..441ec91 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/exports.js @@ -0,0 +1,70 @@ +function validator(expected) { + function validate(module, file) { + if (!module) return; + for (const key in module) { + if (key[0] === "_" || expected.has(key)) continue; + const values = [...expected.values()]; + const hint = hint_for_supported_files(key, file?.slice(file.lastIndexOf("."))) ?? `valid exports are ${values.join(", ")}, or anything with a '_' prefix`; + throw new Error(`Invalid export '${key}'${file ? ` in ${file}` : ""} (${hint})`); + } + } + return validate; +} +function hint_for_supported_files(key, ext = ".js") { + const supported_files = []; + if (valid_layout_exports.has(key)) { + supported_files.push(`+layout${ext}`); + } + if (valid_page_exports.has(key)) { + supported_files.push(`+page${ext}`); + } + if (valid_layout_server_exports.has(key)) { + supported_files.push(`+layout.server${ext}`); + } + if (valid_page_server_exports.has(key)) { + supported_files.push(`+page.server${ext}`); + } + if (valid_server_exports.has(key)) { + supported_files.push(`+server${ext}`); + } + if (supported_files.length > 0) { + return `'${key}' is a valid export in ${supported_files.slice(0, -1).join(", ")}${supported_files.length > 1 ? " or " : ""}${supported_files.at(-1)}`; + } +} +const valid_layout_exports = /* @__PURE__ */ new Set([ + "load", + "prerender", + "csr", + "ssr", + "trailingSlash", + "config" +]); +const valid_page_exports = /* @__PURE__ */ new Set([...valid_layout_exports, "entries"]); +const valid_layout_server_exports = /* @__PURE__ */ new Set([...valid_layout_exports]); +const valid_page_server_exports = /* @__PURE__ */ new Set([...valid_layout_server_exports, "actions", "entries"]); +const valid_server_exports = /* @__PURE__ */ new Set([ + "GET", + "POST", + "PATCH", + "PUT", + "DELETE", + "OPTIONS", + "HEAD", + "fallback", + "prerender", + "trailingSlash", + "config", + "entries" +]); +const validate_layout_exports = validator(valid_layout_exports); +const validate_page_exports = validator(valid_page_exports); +const validate_layout_server_exports = validator(valid_layout_server_exports); +const validate_page_server_exports = validator(valid_page_server_exports); +const validate_server_exports = validator(valid_server_exports); +export { + validate_layout_exports as a, + validate_page_server_exports as b, + validate_page_exports as c, + validate_server_exports as d, + validate_layout_server_exports as v +}; diff --git a/.svelte-kit/adapter-node/chunks/false.js b/.svelte-kit/adapter-node/chunks/false.js new file mode 100644 index 0000000..f3effd6 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/false.js @@ -0,0 +1,4 @@ +const DEV = false; +export { + DEV as D +}; diff --git a/.svelte-kit/adapter-node/chunks/index.js b/.svelte-kit/adapter-node/chunks/index.js new file mode 100644 index 0000000..14b637b --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/index.js @@ -0,0 +1,60 @@ +import { n as noop } from "./index3.js"; +import { s as safe_not_equal } from "./root.js"; +import "clsx"; +const subscriber_queue = []; +function readable(value, start) { + return { + subscribe: writable(value, start).subscribe + }; +} +function writable(value, start = noop) { + let stop = null; + const subscribers = /* @__PURE__ */ new Set(); + function set(new_value) { + if (safe_not_equal(value, new_value)) { + value = new_value; + if (stop) { + const run_queue = !subscriber_queue.length; + for (const subscriber of subscribers) { + subscriber[1](); + subscriber_queue.push(subscriber, value); + } + if (run_queue) { + for (let i = 0; i < subscriber_queue.length; i += 2) { + subscriber_queue[i][0](subscriber_queue[i + 1]); + } + subscriber_queue.length = 0; + } + } + } + } + function update(fn) { + set(fn( + /** @type {T} */ + value + )); + } + function subscribe(run, invalidate = noop) { + const subscriber = [run, invalidate]; + subscribers.add(subscriber); + if (subscribers.size === 1) { + stop = start(set, update) || noop; + } + run( + /** @type {T} */ + value + ); + return () => { + subscribers.delete(subscriber); + if (subscribers.size === 0 && stop) { + stop(); + stop = null; + } + }; + } + return { set, update, subscribe }; +} +export { + readable as r, + writable as w +}; diff --git a/.svelte-kit/adapter-node/chunks/index2.js b/.svelte-kit/adapter-node/chunks/index2.js new file mode 100644 index 0000000..2c507a6 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/index2.js @@ -0,0 +1,302 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; +import { pgTable, timestamp, text, boolean, index, uuid, real, integer, jsonb, uniqueIndex } from "drizzle-orm/pg-core"; +import { relations } from "drizzle-orm"; +import { b as private_env } from "./shared-server.js"; +const user = pgTable("user", { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: boolean("email_verified").default(false).notNull(), + image: text("image"), + // ── Staff role: 'player' | 'referee' | 'admin' ── + role: text("role").default("player").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => /* @__PURE__ */ new Date()).notNull() +}); +const session = pgTable( + "session", + { + id: text("id").primaryKey(), + expiresAt: timestamp("expires_at").notNull(), + token: text("token").notNull().unique(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").$onUpdate(() => /* @__PURE__ */ new Date()).notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }) + }, + (table) => [index("session_userId_idx").on(table.userId)] +); +const account = pgTable( + "account", + { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: timestamp("access_token_expires_at"), + refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), + scope: text("scope"), + password: text("password"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").$onUpdate(() => /* @__PURE__ */ new Date()).notNull() + }, + (table) => [index("account_userId_idx").on(table.userId)] +); +const verification = pgTable( + "verification", + { + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: timestamp("expires_at").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => /* @__PURE__ */ new Date()).notNull() + }, + (table) => [index("verification_identifier_idx").on(table.identifier)] +); +const userRelations = relations(user, ({ many }) => ({ + sessions: many(session), + accounts: many(account) +})); +const sessionRelations = relations(session, ({ one }) => ({ + user: one(user, { + fields: [session.userId], + references: [user.id] + }) +})); +const accountRelations = relations(account, ({ one }) => ({ + user: one(user, { + fields: [account.userId], + references: [user.id] + }) +})); +const team = pgTable("team", { + id: uuid("id").primaryKey().defaultRandom(), + name: text("name").notNull(), + isPersonal: boolean("is_personal").default(false).notNull(), + ownerId: text("owner_id"), + avatarUrl: text("avatar_url"), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull() +}); +const teamMember = pgTable("team_member", { + id: uuid("id").primaryKey().defaultRandom(), + teamId: uuid("team_id").notNull().references(() => team.id, { onDelete: "cascade" }), + userId: text("user_id").notNull(), + role: text("role").default("member"), + joinedAt: timestamp("joined_at", { withTimezone: true }).defaultNow() +}); +const mappool = pgTable("mappool", { + id: uuid("id").primaryKey().defaultRandom(), + name: text("name").notNull(), + createdBy: text("created_by"), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull() +}); +const mappoolSlot = pgTable("mappool_slot", { + id: uuid("id").primaryKey().defaultRandom(), + mappoolId: uuid("mappool_id").notNull().references(() => mappool.id, { onDelete: "cascade" }), + category: text("category").notNull(), + orderInCategory: integer("order_in_category").notNull(), + beatmapId: text("beatmap_id").notNull(), + starRating: real("star_rating"), + mods: text("mods").array().default([]).notNull() +}); +const match = pgTable( + "match", + { + id: uuid("id").primaryKey().defaultRandom(), + name: text("name"), + state: text("state").default("CREATED").notNull(), + config: jsonb("config").notNull(), + mappoolId: uuid("mappool_id").references(() => mappool.id), + osuLobbyId: integer("osu_lobby_id"), + scheduledAt: timestamp("scheduled_at", { withTimezone: true }), + startedAt: timestamp("started_at", { withTimezone: true }), + finishedAt: timestamp("finished_at", { withTimezone: true }), + winnerId: uuid("winner_id").references(() => team.id), + createdBy: text("created_by"), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull() + }, + (t) => [index("idx_match_state").on(t.state)] +); +const matchParticipant = pgTable( + "match_participant", + { + id: uuid("id").primaryKey().defaultRandom(), + matchId: uuid("match_id").notNull().references(() => match.id, { onDelete: "cascade" }), + teamId: uuid("team_id").notNull().references(() => team.id), + slot: integer("slot").notNull(), + score: integer("score").default(0).notNull(), + rollValue: integer("roll_value"), + pickOrder: integer("pick_order") + }, + (t) => [ + index("idx_mp_match_team").on(t.matchId, t.teamId), + uniqueIndex("idx_mp_match_slot").on(t.matchId, t.slot), + index("idx_mp_match").on(t.matchId) + ] +); +const matchParticipantPlayer = pgTable( + "match_participant_player", + { + id: uuid("id").primaryKey().defaultRandom(), + participantId: uuid("participant_id").notNull().references(() => matchParticipant.id, { onDelete: "cascade" }), + userId: text("user_id").notNull() + }, + (t) => [ + uniqueIndex("idx_mpp_participant_user").on(t.participantId, t.userId), + index("idx_mpp_participant").on(t.participantId) + ] +); +const matchGame = pgTable( + "match_game", + { + id: uuid("id").primaryKey().defaultRandom(), + matchId: uuid("match_id").notNull().references(() => match.id, { onDelete: "cascade" }), + gameNumber: integer("game_number").notNull(), + mappoolSlotId: uuid("mappool_slot_id").notNull().references(() => mappoolSlot.id), + pickedByParticipantId: uuid("picked_by_participant_id").references(() => matchParticipant.id), + winnerParticipantId: uuid("winner_participant_id").references(() => matchParticipant.id), + state: text("state").default("PENDING").notNull(), + startedAt: timestamp("started_at", { withTimezone: true }), + finishedAt: timestamp("finished_at", { withTimezone: true }) + }, + (t) => [index("idx_match_game_match").on(t.matchId)] +); +const matchGameScore = pgTable( + "match_game_score", + { + id: uuid("id").primaryKey().defaultRandom(), + matchGameId: uuid("match_game_id").notNull().references(() => matchGame.id, { onDelete: "cascade" }), + playerId: uuid("player_id").notNull().references(() => matchParticipantPlayer.id), + score: integer("score").default(0).notNull(), + accuracy: real("accuracy").default(0).notNull(), + maxCombo: integer("max_combo").default(0).notNull(), + count300: integer("count_300").default(0).notNull(), + count100: integer("count_100").default(0).notNull(), + count50: integer("count_50").default(0).notNull(), + countMiss: integer("count_miss").default(0).notNull(), + mods: text("mods").array().default([]).notNull(), + passed: boolean("passed").default(false).notNull() + }, + (t) => [index("idx_mgs_game").on(t.matchGameId)] +); +const matchQueue = pgTable( + "match_queue", + { + id: uuid("id").primaryKey().defaultRandom(), + userId: text("user_id").notNull(), + teamId: uuid("team_id").notNull().references(() => team.id), + elo: integer("elo").default(1e3).notNull(), + joinedAt: timestamp("joined_at", { withTimezone: true }).defaultNow().notNull() + }, + (t) => [ + uniqueIndex("idx_queue_user").on(t.userId), + index("idx_queue_elo").on(t.elo) + ] +); +const playerRating = pgTable("player_rating", { + id: uuid("id").primaryKey().defaultRandom(), + userId: text("user_id").notNull().unique(), + elo: integer("elo").default(1e3).notNull(), + wins: integer("wins").default(0).notNull(), + losses: integer("losses").default(0).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull() +}); +const teamRelations = relations(team, ({ many }) => ({ + members: many(teamMember) +})); +const teamMemberRelations = relations(teamMember, ({ one }) => ({ + team: one(team, { fields: [teamMember.teamId], references: [team.id] }) +})); +const mappoolRelations = relations(mappool, ({ many }) => ({ + slots: many(mappoolSlot) +})); +const mappoolSlotRelations = relations(mappoolSlot, ({ one }) => ({ + mappool: one(mappool, { fields: [mappoolSlot.mappoolId], references: [mappool.id] }) +})); +const matchRelations = relations(match, ({ one, many }) => ({ + mappool: one(mappool, { fields: [match.mappoolId], references: [mappool.id] }), + participants: many(matchParticipant), + games: many(matchGame) +})); +const matchParticipantRelations = relations(matchParticipant, ({ one, many }) => ({ + match: one(match, { fields: [matchParticipant.matchId], references: [match.id] }), + team: one(team, { fields: [matchParticipant.teamId], references: [team.id] }), + players: many(matchParticipantPlayer) +})); +const matchParticipantPlayerRelations = relations(matchParticipantPlayer, ({ one }) => ({ + participant: one(matchParticipant, { + fields: [matchParticipantPlayer.participantId], + references: [matchParticipant.id] + }) +})); +const matchGameRelations = relations(matchGame, ({ one, many }) => ({ + match: one(match, { fields: [matchGame.matchId], references: [match.id] }), + slot: one(mappoolSlot, { fields: [matchGame.mappoolSlotId], references: [mappoolSlot.id] }), + scores: many(matchGameScore) +})); +const matchGameScoreRelations = relations(matchGameScore, ({ one }) => ({ + game: one(matchGame, { fields: [matchGameScore.matchGameId], references: [matchGame.id] }), + player: one(matchParticipantPlayer, { + fields: [matchGameScore.playerId], + references: [matchParticipantPlayer.id] + }) +})); +const matchQueueRelations = relations(matchQueue, ({ one }) => ({ + team: one(team, { fields: [matchQueue.teamId], references: [team.id] }) +})); +const schema = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + account, + accountRelations, + mappool, + mappoolRelations, + mappoolSlot, + mappoolSlotRelations, + match, + matchGame, + matchGameRelations, + matchGameScore, + matchGameScoreRelations, + matchParticipant, + matchParticipantPlayer, + matchParticipantPlayerRelations, + matchParticipantRelations, + matchQueue, + matchQueueRelations, + matchRelations, + playerRating, + session, + sessionRelations, + team, + teamMember, + teamMemberRelations, + teamRelations, + user, + userRelations, + verification +}, Symbol.toStringTag, { value: "Module" })); +if (!private_env.DATABASE_URL) throw new Error("DATABASE_URL is not set"); +const client = postgres(private_env.DATABASE_URL); +const db = drizzle(client, { schema }); +export { + mappoolSlot as a, + match as b, + team as c, + db as d, + matchParticipant as e, + matchParticipantPlayer as f, + matchGame as g, + matchGameScore as h, + matchQueue as i, + account as j, + mappool as m, + playerRating as p, + teamMember as t, + user as u +}; diff --git a/.svelte-kit/adapter-node/chunks/index3.js b/.svelte-kit/adapter-node/chunks/index3.js new file mode 100644 index 0000000..20b173b --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/index3.js @@ -0,0 +1,1249 @@ +import { clsx as clsx$1 } from "clsx"; +import * as devalue from "devalue"; +var is_array = Array.isArray; +var index_of = Array.prototype.indexOf; +var includes = Array.prototype.includes; +var array_from = Array.from; +var define_property = Object.defineProperty; +var get_descriptor = Object.getOwnPropertyDescriptor; +var object_prototype = Object.prototype; +var array_prototype = Array.prototype; +var get_prototype_of = Object.getPrototypeOf; +var is_extensible = Object.isExtensible; +var has_own_property = Object.prototype.hasOwnProperty; +const noop = () => { +}; +function run_all(arr) { + for (var i = 0; i < arr.length; i++) { + arr[i](); + } +} +function deferred() { + var resolve; + var reject; + var promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +} +const DERIVED = 1 << 1; +const EFFECT = 1 << 2; +const RENDER_EFFECT = 1 << 3; +const MANAGED_EFFECT = 1 << 24; +const BLOCK_EFFECT = 1 << 4; +const BRANCH_EFFECT = 1 << 5; +const ROOT_EFFECT = 1 << 6; +const BOUNDARY_EFFECT = 1 << 7; +const CONNECTED = 1 << 9; +const CLEAN = 1 << 10; +const DIRTY = 1 << 11; +const MAYBE_DIRTY = 1 << 12; +const INERT = 1 << 13; +const DESTROYED = 1 << 14; +const REACTION_RAN = 1 << 15; +const EFFECT_TRANSPARENT = 1 << 16; +const EAGER_EFFECT = 1 << 17; +const HEAD_EFFECT = 1 << 18; +const EFFECT_PRESERVED = 1 << 19; +const USER_EFFECT = 1 << 20; +const WAS_MARKED = 1 << 16; +const REACTION_IS_UPDATING = 1 << 21; +const ASYNC = 1 << 22; +const ERROR_VALUE = 1 << 23; +const STATE_SYMBOL = /* @__PURE__ */ Symbol("$state"); +const LEGACY_PROPS = /* @__PURE__ */ Symbol("legacy props"); +const STALE_REACTION = new class StaleReactionError extends Error { + name = "StaleReactionError"; + message = "The reaction that called `getAbortSignal()` was re-run or destroyed"; +}(); +const COMMENT_NODE = 8; +function lifecycle_outside_component(name) { + { + throw new Error(`https://svelte.dev/e/lifecycle_outside_component`); + } +} +const HYDRATION_START = "["; +const HYDRATION_START_ELSE = "[!"; +const HYDRATION_START_FAILED = "[?"; +const HYDRATION_END = "]"; +const HYDRATION_ERROR = {}; +const ELEMENT_IS_NAMESPACED = 1; +const ELEMENT_PRESERVE_ATTRIBUTE_CASE = 1 << 1; +const ELEMENT_IS_INPUT = 1 << 2; +const UNINITIALIZED = /* @__PURE__ */ Symbol(); +const DOM_BOOLEAN_ATTRIBUTES = [ + "allowfullscreen", + "async", + "autofocus", + "autoplay", + "checked", + "controls", + "default", + "disabled", + "formnovalidate", + "indeterminate", + "inert", + "ismap", + "loop", + "multiple", + "muted", + "nomodule", + "novalidate", + "open", + "playsinline", + "readonly", + "required", + "reversed", + "seamless", + "selected", + "webkitdirectory", + "defer", + "disablepictureinpicture", + "disableremoteplayback" +]; +function is_boolean_attribute(name) { + return DOM_BOOLEAN_ATTRIBUTES.includes(name); +} +const PASSIVE_EVENTS = ["touchstart", "touchmove"]; +function is_passive_event(name) { + return PASSIVE_EVENTS.includes(name); +} +const ATTR_REGEX = /[&"<]/g; +const CONTENT_REGEX = /[&<]/g; +function escape_html(value, is_attr) { + const str = String(value ?? ""); + const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX; + pattern.lastIndex = 0; + let escaped = ""; + let last = 0; + while (pattern.test(str)) { + const i = pattern.lastIndex - 1; + const ch = str[i]; + escaped += str.substring(last, i) + (ch === "&" ? "&" : ch === '"' ? """ : "<"); + last = i + 1; + } + return escaped + str.substring(last); +} +const replacements = { + translate: /* @__PURE__ */ new Map([ + [true, "yes"], + [false, "no"] + ]) +}; +function attr(name, value, is_boolean = false) { + if (name === "hidden" && value !== "until-found") { + is_boolean = true; + } + if (value == null || !value && is_boolean) return ""; + const normalized = has_own_property.call(replacements, name) && replacements[name].get(value) || value; + const assignment = is_boolean ? `=""` : `="${escape_html(normalized, true)}"`; + return ` ${name}${assignment}`; +} +function clsx(value) { + if (typeof value === "object") { + return clsx$1(value); + } else { + return value ?? ""; + } +} +const whitespace = [..." \n\r\f \v\uFEFF"]; +function to_class(value, hash, directives) { + var classname = value == null ? "" : "" + value; + if (hash) { + classname = classname ? classname + " " + hash : hash; + } + if (directives) { + for (var key of Object.keys(directives)) { + if (directives[key]) { + classname = classname ? classname + " " + key : key; + } else if (classname.length) { + var len = key.length; + var a = 0; + while ((a = classname.indexOf(key, a)) >= 0) { + var b = a + len; + if ((a === 0 || whitespace.includes(classname[a - 1])) && (b === classname.length || whitespace.includes(classname[b]))) { + classname = (a === 0 ? "" : classname.substring(0, a)) + classname.substring(b + 1); + } else { + a = b; + } + } + } + } + } + return classname === "" ? null : classname; +} +function append_styles(styles, important = false) { + var separator = important ? " !important;" : ";"; + var css = ""; + for (var key of Object.keys(styles)) { + var value = styles[key]; + if (value != null && value !== "") { + css += " " + key + ": " + value + separator; + } + } + return css; +} +function to_css_name(name) { + if (name[0] !== "-" || name[1] !== "-") { + return name.toLowerCase(); + } + return name; +} +function to_style(value, styles) { + if (styles) { + var new_style = ""; + var normal_styles; + var important_styles; + if (Array.isArray(styles)) { + normal_styles = styles[0]; + important_styles = styles[1]; + } else { + normal_styles = styles; + } + if (value) { + value = String(value).replaceAll(/\s*\/\*.*?\*\/\s*/g, "").trim(); + var in_str = false; + var in_apo = 0; + var in_comment = false; + var reserved_names = []; + if (normal_styles) { + reserved_names.push(...Object.keys(normal_styles).map(to_css_name)); + } + if (important_styles) { + reserved_names.push(...Object.keys(important_styles).map(to_css_name)); + } + var start_index = 0; + var name_index = -1; + const len = value.length; + for (var i = 0; i < len; i++) { + var c = value[i]; + if (in_comment) { + if (c === "/" && value[i - 1] === "*") { + in_comment = false; + } + } else if (in_str) { + if (in_str === c) { + in_str = false; + } + } else if (c === "/" && value[i + 1] === "*") { + in_comment = true; + } else if (c === '"' || c === "'") { + in_str = c; + } else if (c === "(") { + in_apo++; + } else if (c === ")") { + in_apo--; + } + if (!in_comment && in_str === false && in_apo === 0) { + if (c === ":" && name_index === -1) { + name_index = i; + } else if (c === ";" || i === len - 1) { + if (name_index !== -1) { + var name = to_css_name(value.substring(start_index, name_index).trim()); + if (!reserved_names.includes(name)) { + if (c !== ";") { + i++; + } + var property = value.substring(start_index, i).trim(); + new_style += " " + property + ";"; + } + } + start_index = i + 1; + name_index = -1; + } + } + } + } + if (normal_styles) { + new_style += append_styles(normal_styles); + } + if (important_styles) { + new_style += append_styles(important_styles, true); + } + new_style = new_style.trim(); + return new_style === "" ? null : new_style; + } + return value == null ? null : String(value); +} +const BLOCK_OPEN = ``; +const BLOCK_CLOSE = ``; +let controller = null; +function abort() { + controller?.abort(STALE_REACTION); + controller = null; +} +function await_invalid() { + const error = new Error(`await_invalid +Encountered asynchronous work while rendering synchronously. +https://svelte.dev/e/await_invalid`); + error.name = "Svelte error"; + throw error; +} +function invalid_csp() { + const error = new Error(`invalid_csp +\`csp.nonce\` was set while \`csp.hash\` was \`true\`. These options cannot be used simultaneously. +https://svelte.dev/e/invalid_csp`); + error.name = "Svelte error"; + throw error; +} +function server_context_required() { + const error = new Error(`server_context_required +Could not resolve \`render\` context. +https://svelte.dev/e/server_context_required`); + error.name = "Svelte error"; + throw error; +} +var ssr_context = null; +function set_ssr_context(v) { + ssr_context = v; +} +function getContext(key) { + const context_map = get_or_init_context_map(); + const result = ( + /** @type {T} */ + context_map.get(key) + ); + return result; +} +function setContext(key, context) { + get_or_init_context_map().set(key, context); + return context; +} +function get_or_init_context_map(name) { + if (ssr_context === null) { + lifecycle_outside_component(); + } + return ssr_context.c ??= new Map(get_parent_context(ssr_context) || void 0); +} +function push(fn) { + ssr_context = { p: ssr_context, c: null, r: null }; +} +function pop() { + ssr_context = /** @type {SSRContext} */ + ssr_context.p; +} +function get_parent_context(ssr_context2) { + let parent = ssr_context2.p; + while (parent !== null) { + const context_map = parent.c; + if (context_map !== null) { + return context_map; + } + parent = parent.p; + } + return null; +} +function unresolved_hydratable(key, stack) { + { + console.warn(`https://svelte.dev/e/unresolved_hydratable`); + } +} +function get_render_context() { + const store = als?.getStore(); + { + server_context_required(); + } + return store; +} +let als = null; +let text_encoder; +let crypto; +const obfuscated_import = (module_name) => import( + /* @vite-ignore */ + module_name +); +async function sha256(data) { + text_encoder ??= new TextEncoder(); + crypto ??= globalThis.crypto?.subtle?.digest ? globalThis.crypto : ( + // @ts-ignore - we don't install node types in the prod build + // don't use import('node:crypto') directly because static analysers will think we rely on node when we don't + (await obfuscated_import("node:crypto")).webcrypto + ); + const hash_buffer = await crypto.subtle.digest("SHA-256", text_encoder.encode(data)); + return base64_encode(hash_buffer); +} +function base64_encode(bytes) { + if (globalThis.Buffer) { + return globalThis.Buffer.from(bytes).toString("base64"); + } + let binary = ""; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} +class Renderer { + /** + * The contents of the renderer. + * @type {RendererItem[]} + */ + #out = []; + /** + * Any `onDestroy` callbacks registered during execution of this renderer. + * @type {(() => void)[] | undefined} + */ + #on_destroy = void 0; + /** + * Whether this renderer is a component body. + * @type {boolean} + */ + #is_component_body = false; + /** + * If set, this renderer is an error boundary. When async collection + * of the children fails, the failed snippet is rendered instead. + * @type {{ + * failed: (renderer: Renderer, error: unknown, reset: () => void) => void; + * transformError: (error: unknown) => unknown; + * context: SSRContext | null; + * } | null} + */ + #boundary = null; + /** + * The type of string content that this renderer is accumulating. + * @type {RendererType} + */ + type; + /** @type {Renderer | undefined} */ + #parent; + /** + * Asynchronous work associated with this renderer + * @type {Promise | undefined} + */ + promise = void 0; + /** + * State which is associated with the content tree as a whole. + * It will be re-exposed, uncopied, on all children. + * @type {SSRState} + * @readonly + */ + global; + /** + * State that is local to the branch it is declared in. + * It will be shallow-copied to all children. + * + * @type {{ select_value: string | undefined }} + */ + local; + /** + * @param {SSRState} global + * @param {Renderer | undefined} [parent] + */ + constructor(global, parent) { + this.#parent = parent; + this.global = global; + this.local = parent ? { ...parent.local } : { select_value: void 0 }; + this.type = parent ? parent.type : "body"; + } + /** + * @param {(renderer: Renderer) => void} fn + */ + head(fn) { + const head = new Renderer(this.global, this); + head.type = "head"; + this.#out.push(head); + head.child(fn); + } + /** + * @param {Array>} blockers + * @param {(renderer: Renderer) => void} fn + */ + async_block(blockers, fn) { + this.#out.push(BLOCK_OPEN); + this.async(blockers, fn); + this.#out.push(BLOCK_CLOSE); + } + /** + * @param {Array>} blockers + * @param {(renderer: Renderer) => void} fn + */ + async(blockers, fn) { + let callback = fn; + if (blockers.length > 0) { + const context = ssr_context; + callback = (renderer) => { + return Promise.all(blockers).then(() => { + const previous_context = ssr_context; + try { + set_ssr_context(context); + return fn(renderer); + } finally { + set_ssr_context(previous_context); + } + }); + }; + } + this.child(callback); + } + /** + * @param {Array<() => void>} thunks + */ + run(thunks) { + const context = ssr_context; + let promise = Promise.resolve(thunks[0]()); + const promises = [promise]; + for (const fn of thunks.slice(1)) { + promise = promise.then(() => { + const previous_context = ssr_context; + set_ssr_context(context); + try { + return fn(); + } finally { + set_ssr_context(previous_context); + } + }); + promises.push(promise); + } + promise.catch(noop); + this.promise = promise; + return promises; + } + /** + * @param {(renderer: Renderer) => MaybePromise} fn + */ + child_block(fn) { + this.#out.push(BLOCK_OPEN); + this.child(fn); + this.#out.push(BLOCK_CLOSE); + } + /** + * Create a child renderer. The child renderer inherits the state from the parent, + * but has its own content. + * @param {(renderer: Renderer) => MaybePromise} fn + */ + child(fn) { + const child = new Renderer(this.global, this); + this.#out.push(child); + const parent = ssr_context; + set_ssr_context({ + ...ssr_context, + p: parent, + c: null, + r: child + }); + const result = fn(child); + set_ssr_context(parent); + if (result instanceof Promise) { + result.catch(noop); + result.finally(() => set_ssr_context(null)).catch(noop); + if (child.global.mode === "sync") { + await_invalid(); + } + child.promise = result; + } + return child; + } + /** + * Render children inside an error boundary. If the children throw and the API-level + * `transformError` transform handles the error (doesn't re-throw), the `failed` snippet is + * rendered instead. Otherwise the error propagates. + * + * @param {{ failed?: (renderer: Renderer, error: unknown, reset: () => void) => void }} props + * @param {(renderer: Renderer) => MaybePromise} children_fn + */ + boundary(props, children_fn) { + const child = new Renderer(this.global, this); + this.#out.push(child); + const parent_context = ssr_context; + if (props.failed) { + child.#boundary = { + failed: props.failed, + transformError: this.global.transformError, + context: parent_context + }; + } + set_ssr_context({ + ...ssr_context, + p: parent_context, + c: null, + r: child + }); + try { + const result = children_fn(child); + set_ssr_context(parent_context); + if (result instanceof Promise) { + if (child.global.mode === "sync") { + await_invalid(); + } + result.catch(noop); + child.promise = result; + } + } catch (error) { + set_ssr_context(parent_context); + const failed_snippet = props.failed; + if (!failed_snippet) throw error; + const result = this.global.transformError(error); + child.#out.length = 0; + child.#boundary = null; + if (result instanceof Promise) { + if (this.global.mode === "sync") { + await_invalid(); + } + child.promise = /** @type {Promise} */ + result.then((transformed) => { + set_ssr_context(parent_context); + child.#out.push(Renderer.#serialize_failed_boundary(transformed)); + failed_snippet(child, transformed, noop); + child.#out.push(BLOCK_CLOSE); + }); + child.promise.catch(noop); + } else { + child.#out.push(Renderer.#serialize_failed_boundary(result)); + failed_snippet(child, result, noop); + child.#out.push(BLOCK_CLOSE); + } + } + } + /** + * Create a component renderer. The component renderer inherits the state from the parent, + * but has its own content. It is treated as an ordering boundary for ondestroy callbacks. + * @param {(renderer: Renderer) => MaybePromise} fn + * @param {Function} [component_fn] + * @returns {void} + */ + component(fn, component_fn) { + push(); + const child = this.child(fn); + child.#is_component_body = true; + pop(); + } + /** + * @param {Record} attrs + * @param {(renderer: Renderer) => void} fn + * @param {string | undefined} [css_hash] + * @param {Record | undefined} [classes] + * @param {Record | undefined} [styles] + * @param {number | undefined} [flags] + * @param {boolean | undefined} [is_rich] + * @returns {void} + */ + select(attrs, fn, css_hash, classes, styles, flags, is_rich) { + const { value, ...select_attrs } = attrs; + this.push(``); + this.child((renderer) => { + renderer.local.select_value = value; + fn(renderer); + }); + this.push(`${is_rich ? "" : ""}`); + } + /** + * @param {Record} attrs + * @param {string | number | boolean | ((renderer: Renderer) => void)} body + * @param {string | undefined} [css_hash] + * @param {Record | undefined} [classes] + * @param {Record | undefined} [styles] + * @param {number | undefined} [flags] + * @param {boolean | undefined} [is_rich] + */ + option(attrs, body, css_hash, classes, styles, flags, is_rich) { + this.#out.push(` { + if (has_own_property.call(attrs, "value")) { + value = attrs.value; + } + if (value === this.local.select_value) { + renderer.#out.push(' selected=""'); + } + renderer.#out.push(`>${body2}${is_rich ? "" : ""}`); + if (head) { + renderer.head((child) => child.push(head)); + } + }; + if (typeof body === "function") { + this.child((renderer) => { + const r = new Renderer(this.global, this); + body(r); + if (this.global.mode === "async") { + return r.#collect_content_async().then((content) => { + close(renderer, content.body.replaceAll("", ""), content); + }); + } else { + const content = r.#collect_content(); + close(renderer, content.body.replaceAll("", ""), content); + } + }); + } else { + close(this, body, { body: escape_html(body) }); + } + } + /** + * @param {(renderer: Renderer) => void} fn + */ + title(fn) { + const path = this.get_path(); + const close = (head) => { + this.global.set_title(head, path); + }; + this.child((renderer) => { + const r = new Renderer(renderer.global, renderer); + fn(r); + if (renderer.global.mode === "async") { + return r.#collect_content_async().then((content) => { + close(content.head); + }); + } else { + const content = r.#collect_content(); + close(content.head); + } + }); + } + /** + * @param {string | (() => Promise)} content + */ + push(content) { + if (typeof content === "function") { + this.child(async (renderer) => renderer.push(await content())); + } else { + this.#out.push(content); + } + } + /** + * @param {() => void} fn + */ + on_destroy(fn) { + (this.#on_destroy ??= []).push(fn); + } + /** + * @returns {number[]} + */ + get_path() { + return this.#parent ? [...this.#parent.get_path(), this.#parent.#out.indexOf(this)] : []; + } + /** + * @deprecated this is needed for legacy component bindings + */ + copy() { + const copy = new Renderer(this.global, this.#parent); + copy.#out = this.#out.map((item) => item instanceof Renderer ? item.copy() : item); + copy.promise = this.promise; + return copy; + } + /** + * @param {Renderer} other + * @deprecated this is needed for legacy component bindings + */ + subsume(other) { + if (this.global.mode !== other.global.mode) { + throw new Error( + "invariant: A renderer cannot switch modes. If you're seeing this, there's a compiler bug. File an issue!" + ); + } + this.local = other.local; + this.#out = other.#out.map((item) => { + if (item instanceof Renderer) { + item.subsume(item); + } + return item; + }); + this.promise = other.promise; + this.type = other.type; + } + get length() { + return this.#out.length; + } + /** + * Creates the hydration comment that marks the start of a failed boundary. + * The error is JSON-serialized and embedded inside an HTML comment for the client + * to parse during hydration. The JSON is escaped to prevent `-->` or ``; + } + /** + * Only available on the server and when compiling with the `server` option. + * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. + * @template {Record} Props + * @param {Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp }} [options] + * @returns {RenderOutput} + */ + static render(component, options = {}) { + let sync; + const result = ( + /** @type {RenderOutput} */ + {} + ); + Object.defineProperties(result, { + html: { + get: () => { + return (sync ??= Renderer.#render(component, options)).body; + } + }, + head: { + get: () => { + return (sync ??= Renderer.#render(component, options)).head; + } + }, + body: { + get: () => { + return (sync ??= Renderer.#render(component, options)).body; + } + }, + hashes: { + value: { + script: "" + } + }, + then: { + value: ( + /** + * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function. + * + * @template TResult1 + * @template [TResult2=never] + * @param { (value: SyncRenderOutput) => TResult1 } onfulfilled + * @param { (reason: unknown) => TResult2 } onrejected + */ + (onfulfilled, onrejected) => { + { + const result2 = sync ??= Renderer.#render(component, options); + const user_result = onfulfilled({ + head: result2.head, + body: result2.body, + html: result2.body, + hashes: { script: [] } + }); + return Promise.resolve(user_result); + } + } + ) + } + }); + return result; + } + /** + * Collect all of the `onDestroy` callbacks registered during rendering. In an async context, this is only safe to call + * after awaiting `collect_async`. + * + * Child renderers are "porous" and don't affect execution order, but component body renderers + * create ordering boundaries. Within a renderer, callbacks run in order until hitting a component boundary. + * @returns {Iterable<() => void>} + */ + *#collect_on_destroy() { + for (const component of this.#traverse_components()) { + yield* component.#collect_ondestroy(); + } + } + /** + * Performs a depth-first search of renderers, yielding the deepest components first, then additional components as we backtrack up the tree. + * @returns {Iterable} + */ + *#traverse_components() { + for (const child of this.#out) { + if (typeof child !== "string") { + yield* child.#traverse_components(); + } + } + if (this.#is_component_body) { + yield this; + } + } + /** + * @returns {Iterable<() => void>} + */ + *#collect_ondestroy() { + if (this.#on_destroy) { + for (const fn of this.#on_destroy) { + yield fn; + } + } + for (const child of this.#out) { + if (child instanceof Renderer && !child.#is_component_body) { + yield* child.#collect_ondestroy(); + } + } + } + /** + * Render a component. Throws if any of the children are performing asynchronous work. + * + * @template {Record} Props + * @param {Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string }} options + * @returns {AccumulatedContent} + */ + static #render(component, options) { + var previous_context = ssr_context; + try { + const renderer = Renderer.#open_render("sync", component, options); + const content = renderer.#collect_content(); + return Renderer.#close_render(content, renderer); + } finally { + abort(); + set_ssr_context(previous_context); + } + } + /** + * Render a component. + * + * @template {Record} Props + * @param {Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp }} options + * @returns {Promise} + */ + static async #render_async(component, options) { + const previous_context = ssr_context; + try { + const renderer = Renderer.#open_render("async", component, options); + const content = await renderer.#collect_content_async(); + const hydratables = await renderer.#collect_hydratables(); + if (hydratables !== null) { + content.head = hydratables + content.head; + } + return Renderer.#close_render(content, renderer); + } finally { + set_ssr_context(previous_context); + abort(); + } + } + /** + * Collect all of the code from the `out` array and return it as a string, or a promise resolving to a string. + * @param {AccumulatedContent} content + * @returns {AccumulatedContent} + */ + #collect_content(content = { head: "", body: "" }) { + for (const item of this.#out) { + if (typeof item === "string") { + content[this.type] += item; + } else if (item instanceof Renderer) { + item.#collect_content(content); + } + } + return content; + } + /** + * Collect all of the code from the `out` array and return it as a string. + * @param {AccumulatedContent} content + * @returns {Promise} + */ + async #collect_content_async(content = { head: "", body: "" }) { + await this.promise; + for (const item of this.#out) { + if (typeof item === "string") { + content[this.type] += item; + } else if (item instanceof Renderer) { + if (item.#boundary) { + const boundary_content = { head: "", body: "" }; + try { + await item.#collect_content_async(boundary_content); + content.head += boundary_content.head; + content.body += boundary_content.body; + } catch (error) { + const { context, failed, transformError } = item.#boundary; + set_ssr_context(context); + let transformed = await transformError(error); + const failed_renderer = new Renderer(item.global, item); + failed_renderer.type = item.type; + failed_renderer.#out.push(Renderer.#serialize_failed_boundary(transformed)); + failed(failed_renderer, transformed, noop); + failed_renderer.#out.push(BLOCK_CLOSE); + await failed_renderer.#collect_content_async(content); + } + } else { + await item.#collect_content_async(content); + } + } + } + return content; + } + async #collect_hydratables() { + const ctx = get_render_context().hydratable; + for (const [_, key] of ctx.unresolved_promises) { + unresolved_hydratable(key, ctx.lookup.get(key)?.stack ?? ""); + } + for (const comparison of ctx.comparisons) { + await comparison; + } + return await this.#hydratable_block(ctx); + } + /** + * @template {Record} Props + * @param {'sync' | 'async'} mode + * @param {import('svelte').Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options + * @returns {Renderer} + */ + static #open_render(mode, component, options) { + var previous_context = ssr_context; + try { + const renderer = new Renderer( + new SSRState( + mode, + options.idPrefix ? options.idPrefix + "-" : "", + options.csp, + options.transformError + ) + ); + const context = { p: null, c: options.context ?? null, r: renderer }; + set_ssr_context(context); + renderer.push(BLOCK_OPEN); + component(renderer, options.props ?? {}); + renderer.push(BLOCK_CLOSE); + return renderer; + } finally { + set_ssr_context(previous_context); + } + } + /** + * @param {AccumulatedContent} content + * @param {Renderer} renderer + * @returns {AccumulatedContent & { hashes: { script: Sha256Source[] } }} + */ + static #close_render(content, renderer) { + for (const cleanup of renderer.#collect_on_destroy()) { + cleanup(); + } + let head = content.head + renderer.global.get_title(); + let body = content.body; + for (const { hash, code } of renderer.global.css) { + head += ``; + } + return { + head, + body, + hashes: { + script: renderer.global.csp.script_hashes + } + }; + } + /** + * @param {HydratableContext} ctx + */ + async #hydratable_block(ctx) { + if (ctx.lookup.size === 0) { + return null; + } + let entries = []; + let has_promises = false; + for (const [k, v] of ctx.lookup) { + if (v.promises) { + has_promises = true; + for (const p of v.promises) await p; + } + entries.push(`[${devalue.uneval(k)},${v.serialized}]`); + } + let prelude = `const h = (window.__svelte ??= {}).h ??= new Map();`; + if (has_promises) { + prelude = `const r = (v) => Promise.resolve(v); + ${prelude}`; + } + const body = ` + { + ${prelude} + + for (const [k, v] of [ + ${entries.join(",\n ")} + ]) { + h.set(k, v); + } + } + `; + let csp_attr = ""; + if (this.global.csp.nonce) { + csp_attr = ` nonce="${this.global.csp.nonce}"`; + } else if (this.global.csp.hash) { + const hash = await sha256(body); + this.global.csp.script_hashes.push(`sha256-${hash}`); + } + return ` + ${body}<\/script>`; + } +} +class SSRState { + /** @readonly @type {Csp & { script_hashes: Sha256Source[] }} */ + csp; + /** @readonly @type {'sync' | 'async'} */ + mode; + /** @readonly @type {() => string} */ + uid; + /** @readonly @type {Set<{ hash: string; code: string }>} */ + css = /* @__PURE__ */ new Set(); + /** + * `transformError` passed to `render`. Called when an error boundary catches an error. + * Throws by default if unset in `render`. + * @type {(error: unknown) => unknown} + */ + transformError; + /** @type {{ path: number[], value: string }} */ + #title = { path: [], value: "" }; + /** + * @param {'sync' | 'async'} mode + * @param {string} id_prefix + * @param {Csp} csp + * @param {((error: unknown) => unknown) | undefined} [transformError] + */ + constructor(mode, id_prefix = "", csp = { hash: false }, transformError) { + this.mode = mode; + this.csp = { ...csp, script_hashes: [] }; + this.transformError = transformError ?? ((error) => { + throw error; + }); + let uid = 1; + this.uid = () => `${id_prefix}s${uid++}`; + } + get_title() { + return this.#title.value; + } + /** + * Performs a depth-first (lexicographic) comparison using the path. Rejects sets + * from earlier than or equal to the current value. + * @param {string} value + * @param {number[]} path + */ + set_title(value, path) { + const current = this.#title.path; + let i = 0; + let l = Math.min(path.length, current.length); + while (i < l && path[i] === current[i]) i += 1; + if (path[i] === void 0) return; + if (current[i] === void 0 || path[i] > current[i]) { + this.#title.path = path; + this.#title.value = value; + } + } +} +const INVALID_ATTR_NAME_CHAR_REGEX = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; +function render(component, options = {}) { + if (options.csp?.hash && options.csp.nonce) { + invalid_csp(); + } + return Renderer.render( + /** @type {Component} */ + component, + options + ); +} +function attributes(attrs, css_hash, classes, styles, flags = 0) { + if (styles) { + attrs.style = to_style(attrs.style, styles); + } + if (attrs.class) { + attrs.class = clsx(attrs.class); + } + if (css_hash || classes) { + attrs.class = to_class(attrs.class, css_hash, classes); + } + let attr_str = ""; + let name; + const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0; + const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0; + const is_input = (flags & ELEMENT_IS_INPUT) !== 0; + for (name of Object.keys(attrs)) { + if (typeof attrs[name] === "function") continue; + if (name[0] === "$" && name[1] === "$") continue; + if (INVALID_ATTR_NAME_CHAR_REGEX.test(name)) continue; + var value = attrs[name]; + if (lowercase) { + name = name.toLowerCase(); + } + if (name.length > 2 && name.startsWith("on")) continue; + if (is_input) { + if (name === "defaultvalue" || name === "defaultchecked") { + name = name === "defaultvalue" ? "value" : "checked"; + if (attrs[name]) continue; + } + } + attr_str += attr(name, value, is_html && is_boolean_attribute(name)); + } + return attr_str; +} +function stringify(value) { + return typeof value === "string" ? value : value == null ? "" : value + ""; +} +function attr_class(value, hash, directives) { + var result = to_class(value, hash, directives); + return result ? ` class="${escape_html(result, true)}"` : ""; +} +function ensure_array_like(array_like_or_iterator) { + if (array_like_or_iterator) { + return array_like_or_iterator.length !== void 0 ? array_like_or_iterator : Array.from(array_like_or_iterator); + } + return []; +} +function once(get_value) { + let value = ( + /** @type {V} */ + UNINITIALIZED + ); + return () => { + if (value === UNINITIALIZED) { + value = get_value(); + } + return value; + }; +} +function derived(fn) { + const get_value = ssr_context === null ? fn : once(fn); + let updated_value; + return function(new_value) { + if (arguments.length === 0) { + return updated_value ?? get_value(); + } + updated_value = new_value; + return updated_value; + }; +} +export { + is_passive_event as $, + ASYNC as A, + BOUNDARY_EFFECT as B, + COMMENT_NODE as C, + DIRTY as D, + ERROR_VALUE as E, + EFFECT_TRANSPARENT as F, + EFFECT_PRESERVED as G, + HYDRATION_ERROR as H, + INERT as I, + EAGER_EFFECT as J, + STATE_SYMBOL as K, + object_prototype as L, + MAYBE_DIRTY as M, + array_prototype as N, + get_descriptor as O, + get_prototype_of as P, + is_array as Q, + REACTION_RAN as R, + STALE_REACTION as S, + is_extensible as T, + UNINITIALIZED as U, + USER_EFFECT as V, + WAS_MARKED as W, + REACTION_IS_UPDATING as X, + index_of as Y, + define_property as Z, + array_from as _, + ensure_array_like as a, + LEGACY_PROPS as a0, + render as a1, + setContext as a2, + attr as b, + attr_class as c, + derived as d, + escape_html as e, + HYDRATION_END as f, + getContext as g, + HYDRATION_START as h, + HYDRATION_START_ELSE as i, + EFFECT as j, + CONNECTED as k, + CLEAN as l, + DERIVED as m, + noop as n, + BLOCK_EFFECT as o, + deferred as p, + BRANCH_EFFECT as q, + run_all as r, + stringify as s, + ROOT_EFFECT as t, + RENDER_EFFECT as u, + MANAGED_EFFECT as v, + HEAD_EFFECT as w, + DESTROYED as x, + includes as y, + HYDRATION_START_FAILED as z +}; diff --git a/.svelte-kit/adapter-node/chunks/index4.js b/.svelte-kit/adapter-node/chunks/index4.js new file mode 100644 index 0000000..2ae44a6 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/index4.js @@ -0,0 +1,47 @@ +import "./state.svelte.js"; +import "clsx"; +import "@sveltejs/kit/internal"; +import "./url.js"; +import "./utils.js"; +import { w as writable } from "./index.js"; +import "@sveltejs/kit/internal/server"; +import "./root.js"; +import "./exports.js"; +import { g as getContext } from "./index3.js"; +function create_updated_store() { + const { set, subscribe } = writable(false); + { + return { + subscribe, + // eslint-disable-next-line @typescript-eslint/require-await + check: async () => false + }; + } +} +const stores = { + updated: /* @__PURE__ */ create_updated_store() +}; +({ + check: stores.updated.check +}); +function context() { + return getContext("__request__"); +} +const page$1 = { + get data() { + return context().page.data; + }, + get error() { + return context().page.error; + }, + get status() { + return context().page.status; + }, + get url() { + return context().page.url; + } +}; +const page = page$1; +export { + page as p +}; diff --git a/.svelte-kit/adapter-node/chunks/index5.js b/.svelte-kit/adapter-node/chunks/index5.js new file mode 100644 index 0000000..5a5bcdc --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/index5.js @@ -0,0 +1,224 @@ +import { logger } from "@better-auth/core/env"; +import { createAdapterFactory } from "@better-auth/core/db/adapter"; +const memoryAdapter = (db, config) => { + let lazyOptions = null; + const adapterCreator = createAdapterFactory({ + config: { + adapterId: "memory", + adapterName: "Memory Adapter", + usePlural: false, + debugLogs: config?.debugLogs || false, + supportsArrays: true, + customTransformInput(props) { + if ((props.options.advanced?.database?.useNumberId || props.options.advanced?.database?.generateId === "serial") && props.field === "id" && props.action === "create") return db[props.model].length + 1; + return props.data; + }, + transaction: async (cb) => { + const clone = structuredClone(db); + try { + return await cb(adapterCreator(lazyOptions)); + } catch (error) { + Object.keys(db).forEach((key) => { + db[key] = clone[key]; + }); + throw error; + } + } + }, + adapter: ({ getFieldName, getDefaultFieldName, options, getModelName }) => { + const applySortToRecords = (records, sortBy, model) => { + if (!sortBy) return records; + return records.sort((a, b) => { + const field = getFieldName({ + model, + field: sortBy.field + }); + const aValue = a[field]; + const bValue = b[field]; + let comparison = 0; + if (aValue == null && bValue == null) comparison = 0; + else if (aValue == null) comparison = -1; + else if (bValue == null) comparison = 1; + else if (typeof aValue === "string" && typeof bValue === "string") comparison = aValue.localeCompare(bValue); + else if (aValue instanceof Date && bValue instanceof Date) comparison = aValue.getTime() - bValue.getTime(); + else if (typeof aValue === "number" && typeof bValue === "number") comparison = aValue - bValue; + else if (typeof aValue === "boolean" && typeof bValue === "boolean") comparison = aValue === bValue ? 0 : aValue ? 1 : -1; + else comparison = String(aValue).localeCompare(String(bValue)); + return sortBy.direction === "asc" ? comparison : -comparison; + }); + }; + function convertWhereClause(where, model, join, select) { + const baseRecords = (() => { + const table = db[model]; + if (!table) { + logger.error(`[MemoryAdapter] Model ${model} not found in the DB`, Object.keys(db)); + throw new Error(`Model ${model} not found`); + } + const evalClause = (record, clause) => { + const { field, value, operator } = clause; + switch (operator) { + case "in": + if (!Array.isArray(value)) throw new Error("Value must be an array"); + return value.includes(record[field]); + case "not_in": + if (!Array.isArray(value)) throw new Error("Value must be an array"); + return !value.includes(record[field]); + case "contains": + return record[field].includes(value); + case "starts_with": + return record[field].startsWith(value); + case "ends_with": + return record[field].endsWith(value); + case "ne": + return record[field] !== value; + case "gt": + return value != null && Boolean(record[field] > value); + case "gte": + return value != null && Boolean(record[field] >= value); + case "lt": + return value != null && Boolean(record[field] < value); + case "lte": + return value != null && Boolean(record[field] <= value); + default: + return record[field] === value; + } + }; + let records = table.filter((record) => { + if (!where.length || where.length === 0) return true; + let result = evalClause(record, where[0]); + for (const clause of where) { + const clauseResult = evalClause(record, clause); + if (clause.connector === "OR") result = result || clauseResult; + else result = result && clauseResult; + } + return result; + }); + if (select?.length && select.length > 0) records = records.map((record) => Object.fromEntries(Object.entries(record).filter(([key]) => select.includes(getDefaultFieldName({ + model, + field: key + }))))); + return records; + })(); + if (!join) return baseRecords; + const grouped = /* @__PURE__ */ new Map(); + const seenIds = /* @__PURE__ */ new Map(); + for (const baseRecord of baseRecords) { + const baseId = String(baseRecord.id); + if (!grouped.has(baseId)) { + const nested = { ...baseRecord }; + for (const [joinModel, joinAttr] of Object.entries(join)) { + const joinModelName = getModelName(joinModel); + if (joinAttr.relation === "one-to-one") nested[joinModelName] = null; + else { + nested[joinModelName] = []; + seenIds.set(`${baseId}-${joinModel}`, /* @__PURE__ */ new Set()); + } + } + grouped.set(baseId, nested); + } + const nestedEntry = grouped.get(baseId); + for (const [joinModel, joinAttr] of Object.entries(join)) { + const joinModelName = getModelName(joinModel); + const joinTable = db[joinModelName]; + if (!joinTable) { + logger.error(`[MemoryAdapter] JoinOption model ${joinModelName} not found in the DB`, Object.keys(db)); + throw new Error(`JoinOption model ${joinModelName} not found`); + } + const matchingRecords = joinTable.filter((joinRecord) => joinRecord[joinAttr.on.to] === baseRecord[joinAttr.on.from]); + if (joinAttr.relation === "one-to-one") nestedEntry[joinModelName] = matchingRecords[0] || null; + else { + const seenSet = seenIds.get(`${baseId}-${joinModel}`); + const limit = joinAttr.limit ?? 100; + let count = 0; + for (const matchingRecord of matchingRecords) { + if (count >= limit) break; + if (!seenSet.has(matchingRecord.id)) { + nestedEntry[joinModelName].push(matchingRecord); + seenSet.add(matchingRecord.id); + count++; + } + } + } + } + } + return Array.from(grouped.values()); + } + return { + create: async ({ model, data }) => { + if (options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial") data.id = db[getModelName(model)].length + 1; + if (!db[model]) db[model] = []; + db[model].push(data); + return data; + }, + findOne: async ({ model, where, select, join }) => { + const res = convertWhereClause(where, model, join, select); + if (join) { + const resArray = res; + if (!resArray.length) return null; + return resArray[0]; + } + return res[0] || null; + }, + findMany: async ({ model, where, sortBy, limit, select, offset, join }) => { + const res = convertWhereClause(where || [], model, join, select); + if (join) { + const resArray = res; + if (!resArray.length) return []; + applySortToRecords(resArray, sortBy, model); + let paginatedRecords = resArray; + if (offset !== void 0) paginatedRecords = paginatedRecords.slice(offset); + if (limit !== void 0) paginatedRecords = paginatedRecords.slice(0, limit); + return paginatedRecords; + } + let table = applySortToRecords(res, sortBy, model); + if (offset !== void 0) table = table.slice(offset); + if (limit !== void 0) table = table.slice(0, limit); + return table || []; + }, + count: async ({ model, where }) => { + if (where) return convertWhereClause(where, model).length; + return db[model].length; + }, + update: async ({ model, where, update }) => { + const res = convertWhereClause(where, model); + res.forEach((record) => { + Object.assign(record, update); + }); + return res[0] || null; + }, + delete: async ({ model, where }) => { + const table = db[model]; + const res = convertWhereClause(where, model); + db[model] = table.filter((record) => !res.includes(record)); + }, + deleteMany: async ({ model, where }) => { + const table = db[model]; + const res = convertWhereClause(where, model); + let count = 0; + db[model] = table.filter((record) => { + if (res.includes(record)) { + count++; + return false; + } + return !res.includes(record); + }); + return count; + }, + updateMany({ model, where, update }) { + const res = convertWhereClause(where, model); + res.forEach((record) => { + Object.assign(record, update); + }); + return res[0] || null; + } + }; + } + }); + return (options) => { + lazyOptions = options; + return adapterCreator(options); + }; +}; +export { + memoryAdapter +}; diff --git a/.svelte-kit/adapter-node/chunks/index6.js b/.svelte-kit/adapter-node/chunks/index6.js new file mode 100644 index 0000000..f55374c --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/index6.js @@ -0,0 +1,307 @@ +import { c as createKyselyAdapter } from "./auth.js"; +import { g } from "./auth.js"; +import { sql } from "kysely"; +import { createAdapterFactory } from "@better-auth/core/db/adapter"; +const kyselyAdapter = (db, config) => { + let lazyOptions = null; + const createCustomAdapter = (db$1) => { + return ({ getFieldName, schema, getDefaultFieldName, getDefaultModelName, getFieldAttributes, getModelName }) => { + const selectAllJoins = (join) => { + const allSelects = []; + const allSelectsStr = []; + if (join) for (const [joinModel, _] of Object.entries(join)) { + const fields = schema[getDefaultModelName(joinModel)]?.fields; + const [_joinModelSchema, joinModelName] = joinModel.includes(".") ? joinModel.split(".") : [void 0, joinModel]; + if (!fields) continue; + fields.id = { type: "string" }; + for (const [field, fieldAttr] of Object.entries(fields)) { + allSelects.push(sql`${sql.ref(`join_${joinModelName}`)}.${sql.ref(fieldAttr.fieldName || field)} as ${sql.ref(`_joined_${joinModelName}_${fieldAttr.fieldName || field}`)}`); + allSelectsStr.push({ + joinModel, + joinModelRef: joinModelName, + fieldName: fieldAttr.fieldName || field + }); + } + } + return { + allSelectsStr, + allSelects + }; + }; + const withReturning = async (values, builder, model, where) => { + let res; + if (config?.type === "mysql") { + await builder.execute(); + const field = values.id ? "id" : where.length > 0 && where[0]?.field ? where[0].field : "id"; + if (!values.id && where.length === 0) { + res = await db$1.selectFrom(model).selectAll().orderBy(getFieldName({ + model, + field + }), "desc").limit(1).executeTakeFirst(); + return res; + } + const value = values[field] || where[0]?.value; + res = await db$1.selectFrom(model).selectAll().orderBy(getFieldName({ + model, + field + }), "desc").where(getFieldName({ + model, + field + }), "=", value).limit(1).executeTakeFirst(); + return res; + } + if (config?.type === "mssql") { + res = await builder.outputAll("inserted").executeTakeFirst(); + return res; + } + res = await builder.returningAll().executeTakeFirst(); + return res; + }; + function convertWhereClause(model, w) { + if (!w) return { + and: null, + or: null + }; + const conditions = { + and: [], + or: [] + }; + w.forEach((condition) => { + const { field: _field, value: _value, operator = "=", connector = "AND" } = condition; + const value = _value; + const field = getFieldName({ + model, + field: _field + }); + const expr = (eb) => { + const f = `${model}.${field}`; + if (operator.toLowerCase() === "in") return eb(f, "in", Array.isArray(value) ? value : [value]); + if (operator.toLowerCase() === "not_in") return eb(f, "not in", Array.isArray(value) ? value : [value]); + if (operator === "contains") return eb(f, "like", `%${value}%`); + if (operator === "starts_with") return eb(f, "like", `${value}%`); + if (operator === "ends_with") return eb(f, "like", `%${value}`); + if (operator === "eq") return eb(f, "=", value); + if (operator === "ne") return eb(f, "<>", value); + if (operator === "gt") return eb(f, ">", value); + if (operator === "gte") return eb(f, ">=", value); + if (operator === "lt") return eb(f, "<", value); + if (operator === "lte") return eb(f, "<=", value); + return eb(f, operator, value); + }; + if (connector === "OR") conditions.or.push(expr); + else conditions.and.push(expr); + }); + return { + and: conditions.and.length ? conditions.and : null, + or: conditions.or.length ? conditions.or : null + }; + } + function processJoinedResults(rows, joinConfig, allSelectsStr) { + if (!joinConfig || !rows.length) return rows; + const groupedByMainId = /* @__PURE__ */ new Map(); + for (const currentRow of rows) { + const mainModelFields = {}; + const joinedModelFields = {}; + for (const [joinModel] of Object.entries(joinConfig)) joinedModelFields[getModelName(joinModel)] = {}; + for (const [key, value] of Object.entries(currentRow)) { + const keyStr = String(key); + let assigned = false; + for (const { joinModel, fieldName, joinModelRef } of allSelectsStr) if (keyStr === `_joined_${joinModelRef}_${fieldName}`) { + joinedModelFields[getModelName(joinModel)][getFieldName({ + model: joinModel, + field: fieldName + })] = value; + assigned = true; + break; + } + if (!assigned) mainModelFields[key] = value; + } + const mainId = mainModelFields.id; + if (!mainId) continue; + if (!groupedByMainId.has(mainId)) { + const entry$1 = { ...mainModelFields }; + for (const [joinModel, joinAttr] of Object.entries(joinConfig)) entry$1[getModelName(joinModel)] = joinAttr.relation === "one-to-one" ? null : []; + groupedByMainId.set(mainId, entry$1); + } + const entry = groupedByMainId.get(mainId); + for (const [joinModel, joinAttr] of Object.entries(joinConfig)) { + const isUnique = joinAttr.relation === "one-to-one"; + const limit = joinAttr.limit ?? 100; + const joinedObj = joinedModelFields[getModelName(joinModel)]; + const hasData = joinedObj && Object.keys(joinedObj).length > 0 && Object.values(joinedObj).some((value) => value !== null && value !== void 0); + if (isUnique) entry[getModelName(joinModel)] = hasData ? joinedObj : null; + else { + const joinModelName = getModelName(joinModel); + if (Array.isArray(entry[joinModelName]) && hasData) { + if (entry[joinModelName].length >= limit) continue; + const idFieldName = getFieldName({ + model: joinModel, + field: "id" + }); + const joinedId = joinedObj[idFieldName]; + if (joinedId) { + if (!entry[joinModelName].some((item) => item[idFieldName] === joinedId) && entry[joinModelName].length < limit) entry[joinModelName].push(joinedObj); + } else if (entry[joinModelName].length < limit) entry[joinModelName].push(joinedObj); + } + } + } + } + const result = Array.from(groupedByMainId.values()); + for (const entry of result) for (const [joinModel, joinAttr] of Object.entries(joinConfig)) if (joinAttr.relation !== "one-to-one") { + const joinModelName = getModelName(joinModel); + if (Array.isArray(entry[joinModelName])) { + const limit = joinAttr.limit ?? 100; + if (entry[joinModelName].length > limit) entry[joinModelName] = entry[joinModelName].slice(0, limit); + } + } + return result; + } + return { + async create({ data, model }) { + return await withReturning(data, db$1.insertInto(model).values(data), model, []); + }, + async findOne({ model, where, select, join }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.selectFrom((eb) => { + let b = eb.selectFrom(model); + if (and) b = b.where((eb$1) => eb$1.and(and.map((expr) => expr(eb$1)))); + if (or) b = b.where((eb$1) => eb$1.or(or.map((expr) => expr(eb$1)))); + if (select?.length && select.length > 0) b = b.select(select.map((field) => getFieldName({ + model, + field + }))); + else b = b.selectAll(); + return b.as("primary"); + }).selectAll("primary"); + if (join) for (const [joinModel, joinAttr] of Object.entries(join)) { + const [_joinModelSchema, joinModelName] = joinModel.includes(".") ? joinModel.split(".") : [void 0, joinModel]; + query = query.leftJoin(`${joinModel} as join_${joinModelName}`, (join$1) => join$1.onRef(`join_${joinModelName}.${joinAttr.on.to}`, "=", `primary.${joinAttr.on.from}`)); + } + const { allSelectsStr, allSelects } = selectAllJoins(join); + query = query.select(allSelects); + const res = await query.execute(); + if (!res || !Array.isArray(res) || res.length === 0) return null; + const row = res[0]; + if (join) return processJoinedResults(res, join, allSelectsStr)[0]; + return row; + }, + async findMany({ model, where, limit, select, offset, sortBy, join }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.selectFrom((eb) => { + let b = eb.selectFrom(model); + if (config?.type === "mssql") { + if (offset !== void 0) { + if (!sortBy) b = b.orderBy(getFieldName({ + model, + field: "id" + })); + b = b.offset(offset).fetch(limit || 100); + } else if (limit !== void 0) b = b.top(limit); + } else { + if (limit !== void 0) b = b.limit(limit); + if (offset !== void 0) b = b.offset(offset); + } + if (sortBy?.field) b = b.orderBy(`${getFieldName({ + model, + field: sortBy.field + })}`, sortBy.direction); + if (and) b = b.where((eb$1) => eb$1.and(and.map((expr) => expr(eb$1)))); + if (or) b = b.where((eb$1) => eb$1.or(or.map((expr) => expr(eb$1)))); + if (select?.length && select.length > 0) b = b.select(select.map((field) => getFieldName({ + model, + field + }))); + else b = b.selectAll(); + return b.as("primary"); + }).selectAll("primary"); + if (join) for (const [joinModel, joinAttr] of Object.entries(join)) { + const [_joinModelSchema, joinModelName] = joinModel.includes(".") ? joinModel.split(".") : [void 0, joinModel]; + query = query.leftJoin(`${joinModel} as join_${joinModelName}`, (join$1) => join$1.onRef(`join_${joinModelName}.${joinAttr.on.to}`, "=", `primary.${joinAttr.on.from}`)); + } + const { allSelectsStr, allSelects } = selectAllJoins(join); + query = query.select(allSelects); + if (sortBy?.field) query = query.orderBy(`${getFieldName({ + model, + field: sortBy.field + })}`, sortBy.direction); + const res = await query.execute(); + if (!res) return []; + if (join) return processJoinedResults(res, join, allSelectsStr); + return res; + }, + async update({ model, where, update: values }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.updateTable(model).set(values); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + return await withReturning(values, query, model, where); + }, + async updateMany({ model, where, update: values }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.updateTable(model).set(values); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + const res = (await query.executeTakeFirst()).numUpdatedRows; + return res > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : Number(res); + }, + async count({ model, where }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.selectFrom(model).select(db$1.fn.count("id").as("count")); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + const res = await query.execute(); + if (typeof res[0].count === "number") return res[0].count; + if (typeof res[0].count === "bigint") return Number(res[0].count); + return parseInt(res[0].count); + }, + async delete({ model, where }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.deleteFrom(model); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + await query.execute(); + }, + async deleteMany({ model, where }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.deleteFrom(model); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + const res = (await query.executeTakeFirst()).numDeletedRows; + return res > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : Number(res); + }, + options: config + }; + }; + }; + let adapterOptions = null; + adapterOptions = { + config: { + adapterId: "kysely", + adapterName: "Kysely Adapter", + usePlural: config?.usePlural, + debugLogs: config?.debugLogs, + supportsBooleans: config?.type === "sqlite" || config?.type === "mssql" || config?.type === "mysql" || !config?.type ? false : true, + supportsDates: config?.type === "sqlite" || config?.type === "mssql" || !config?.type ? false : true, + supportsJSON: config?.type === "postgres" ? true : false, + supportsArrays: false, + supportsUUIDs: config?.type === "postgres" ? true : false, + transaction: config?.transaction ? (cb) => db.transaction().execute((trx) => { + return cb(createAdapterFactory({ + config: adapterOptions.config, + adapter: createCustomAdapter(trx) + })(lazyOptions)); + }) : false + }, + adapter: createCustomAdapter(db) + }; + const adapter = createAdapterFactory(adapterOptions); + return (options) => { + lazyOptions = options; + return adapter(options); + }; +}; +export { + createKyselyAdapter, + g as getKyselyDatabaseType, + kyselyAdapter +}; diff --git a/.svelte-kit/adapter-node/chunks/internal.js b/.svelte-kit/adapter-node/chunks/internal.js new file mode 100644 index 0000000..55b3eeb --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/internal.js @@ -0,0 +1,127 @@ +import { r as root } from "./root.js"; +import "./environment.js"; +import "./server.js"; +import "./shared-server.js"; +let read_implementation = null; +function set_read_implementation(fn) { + read_implementation = fn; +} +function set_manifest(_) { +} +const options = { + app_template_contains_nonce: false, + async: false, + csp: { "mode": "auto", "directives": { "upgrade-insecure-requests": false, "block-all-mixed-content": false }, "reportOnly": { "upgrade-insecure-requests": false, "block-all-mixed-content": false } }, + csrf_check_origin: true, + csrf_trusted_origins: [], + embedded: false, + env_public_prefix: "PUBLIC_", + env_private_prefix: "", + hash_routing: false, + hooks: null, + // added lazily, via `get_hooks` + preload_strategy: "modulepreload", + root, + service_worker: false, + service_worker_options: void 0, + templates: { + app: ({ head, body, assets, nonce, env }) => '\n\n \n \n \n \n \n \n Vash Esports\n ' + head + '\n \n \n
' + body + "
\n \n\n", + error: ({ status, message }) => '\n\n \n \n ' + message + ` + + + + +
+ ` + status + '\n
\n

' + message + "

\n
\n
\n \n\n" + }, + version_hash: "12t8tv8" +}; +async function get_hooks() { + let handle; + let handleFetch; + let handleError; + let handleValidationError; + let init; + ({ handle, handleFetch, handleError, handleValidationError, init } = await import("../entries/hooks.server.js")); + let reroute; + let transport; + return { + handle, + handleFetch, + handleError, + handleValidationError, + init, + reroute, + transport + }; +} +export { + set_manifest as a, + get_hooks as g, + options as o, + read_implementation as r, + set_read_implementation as s +}; diff --git a/.svelte-kit/adapter-node/chunks/node-sqlite-dialect.js b/.svelte-kit/adapter-node/chunks/node-sqlite-dialect.js new file mode 100644 index 0000000..68f03bc --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/node-sqlite-dialect.js @@ -0,0 +1,155 @@ +import { CompiledQuery, DefaultQueryCompiler, DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE, sql } from "kysely"; +var NodeSqliteAdapter = class { + get supportsCreateIfNotExists() { + return true; + } + get supportsTransactionalDdl() { + return false; + } + get supportsReturning() { + return true; + } + async acquireMigrationLock() { + } + async releaseMigrationLock() { + } + get supportsOutput() { + return true; + } +}; +var NodeSqliteDriver = class { + #config; + #connectionMutex = new ConnectionMutex(); + #db; + #connection; + constructor(config) { + this.#config = { ...config }; + } + async init() { + this.#db = this.#config.database; + this.#connection = new NodeSqliteConnection(this.#db); + if (this.#config.onCreateConnection) await this.#config.onCreateConnection(this.#connection); + } + async acquireConnection() { + await this.#connectionMutex.lock(); + return this.#connection; + } + async beginTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("begin")); + } + async commitTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("commit")); + } + async rollbackTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("rollback")); + } + async releaseConnection() { + this.#connectionMutex.unlock(); + } + async destroy() { + this.#db?.close(); + } +}; +var NodeSqliteConnection = class { + #db; + constructor(db) { + this.#db = db; + } + executeQuery(compiledQuery) { + const { sql: sql$1, parameters } = compiledQuery; + const rows = this.#db.prepare(sql$1).all(...parameters); + return Promise.resolve({ rows }); + } + async *streamQuery() { + throw new Error("Streaming query is not supported by SQLite driver."); + } +}; +var ConnectionMutex = class { + #promise; + #resolve; + async lock() { + while (await this.#promise) await this.#promise; + this.#promise = new Promise((resolve) => { + this.#resolve = resolve; + }); + } + unlock() { + const resolve = this.#resolve; + this.#promise = void 0; + this.#resolve = void 0; + resolve?.(); + } +}; +var NodeSqliteIntrospector = class { + #db; + constructor(db) { + this.#db = db; + } + async getSchemas() { + return []; + } + async getTables(options = { withInternalKyselyTables: false }) { + let query = this.#db.selectFrom("sqlite_schema").where("type", "=", "table").where("name", "not like", "sqlite_%").select("name").$castTo(); + if (!options.withInternalKyselyTables) query = query.where("name", "!=", DEFAULT_MIGRATION_TABLE).where("name", "!=", DEFAULT_MIGRATION_LOCK_TABLE); + const tables = await query.execute(); + return Promise.all(tables.map(({ name }) => this.#getTableMetadata(name))); + } + async getMetadata(options) { + return { tables: await this.getTables(options) }; + } + async #getTableMetadata(table) { + const db = this.#db; + const autoIncrementCol = (await db.selectFrom("sqlite_master").where("name", "=", table).select("sql").$castTo().execute())[0]?.sql?.split(/[\(\),]/)?.find((it) => it.toLowerCase().includes("autoincrement"))?.split(/\s+/)?.[0]?.replace(/["`]/g, ""); + return { + name: table, + columns: (await db.selectFrom(sql`pragma_table_info(${table})`.as("table_info")).select([ + "name", + "type", + "notnull", + "dflt_value" + ]).execute()).map((col) => ({ + name: col.name, + dataType: col.type, + isNullable: !col.notnull, + isAutoIncrementing: col.name === autoIncrementCol, + hasDefaultValue: col.dflt_value != null + })), + isView: true + }; + } +}; +var NodeSqliteQueryCompiler = class extends DefaultQueryCompiler { + getCurrentParameterPlaceholder() { + return "?"; + } + getLeftIdentifierWrapper() { + return '"'; + } + getRightIdentifierWrapper() { + return '"'; + } + getAutoIncrement() { + return "autoincrement"; + } +}; +var NodeSqliteDialect = class { + #config; + constructor(config) { + this.#config = { ...config }; + } + createDriver() { + return new NodeSqliteDriver(this.#config); + } + createQueryCompiler() { + return new NodeSqliteQueryCompiler(); + } + createAdapter() { + return new NodeSqliteAdapter(); + } + createIntrospector(db) { + return new NodeSqliteIntrospector(db); + } +}; +export { + NodeSqliteDialect +}; diff --git a/.svelte-kit/adapter-node/chunks/orchestrator.js b/.svelte-kit/adapter-node/chunks/orchestrator.js new file mode 100644 index 0000000..da259e9 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/orchestrator.js @@ -0,0 +1,448 @@ +import { d as db, b as match, u as user, g as matchGame } from "./index2.js"; +import { eq } from "drizzle-orm"; +import { g as getMatchFull, M as MATCH_STATES, s as submitRoll, p as pickMap, d as submitGameScores } from "./engine.js"; +import { b as private_env } from "./shared-server.js"; +let client = null; +let connecting = null; +async function getClient() { + if (client?.isConnected?.()) return client; + if (connecting) { + await connecting; + return client; + } + if (!private_env.OSU_IRC_USERNAME || !private_env.OSU_IRC_PASSWORD) { + throw new Error("OSU_IRC_USERNAME and OSU_IRC_PASSWORD must be set in .env"); + } + const banchoModule = await import("bancho.js"); + const BanchoClient = banchoModule.BanchoClient ?? banchoModule.default?.BanchoClient ?? banchoModule.default; + client = new BanchoClient({ + username: private_env.OSU_IRC_USERNAME, + password: private_env.OSU_IRC_PASSWORD + }); + connecting = client.connect().then(() => { + console.log("[Bancho] Connected as", private_env.OSU_IRC_USERNAME); + connecting = null; + }); + await connecting; + return client; +} +class TournamentLobby { + channel = null; + channelName = ""; + osuMatchId = 0; + // Score collection + collectedScores = []; + matchFinishedResolve = null; + // Ready tracking + allReadyResolve = null; + // Event callbacks (set by orchestrator) + onRollResult = null; + onPickCommand = null; + /** + * Create a new multiplayer lobby via !mp make. + */ + async create(name) { + const c = await getClient(); + return new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(new Error("Lobby creation timed out")), + 15e3 + ); + const banchoBot = c.getUser("BanchoBot"); + const onPM = (msg) => { + const text = msg.message ?? String(msg); + const m = text.match(/https:\/\/osu\.ppy\.sh\/mp\/(\d+)/); + if (m) { + clearTimeout(timeout); + banchoBot.removeListener("message", onPM); + this.osuMatchId = parseInt(m[1]); + this.channelName = `#mp_${this.osuMatchId}`; + this.joinChannel(c).then( + () => resolve({ matchId: this.osuMatchId, channel: this.channelName }) + ); + } + }; + banchoBot.on("message", onPM); + banchoBot.sendMessage(`!mp make ${name}`); + }); + } + async joinChannel(c) { + this.channel = c.getChannel(this.channelName); + await this.channel.join(); + this.channel.on("message", (msg) => { + const text = msg.message ?? String(msg); + const sender = msg.user?.ircUsername ?? ""; + if (sender === "BanchoBot") { + this.parseBanchoMessage(text); + } else if (sender) { + this.parsePlayerMessage(sender, text); + } + }); + } + // ── Message Parsing ───────────────────────────────────────────────── + parseBanchoMessage(text) { + const scoreMatch = text.match( + /^(.+?) finished playing \(Score: ([\d,]+).*?(PASSED|FAILED)\)/i + ); + if (scoreMatch) { + this.collectedScores.push({ + username: scoreMatch[1].trim(), + score: parseInt(scoreMatch[2].replace(/,/g, "")), + passed: scoreMatch[3] === "PASSED" + }); + console.log(`[Bancho] Score: ${scoreMatch[1].trim()} = ${scoreMatch[2]}`); + return; + } + if (text.includes("The match has finished!")) { + console.log("[Bancho] Game finished. Scores:", this.collectedScores); + if (this.matchFinishedResolve) { + this.matchFinishedResolve([...this.collectedScores]); + this.matchFinishedResolve = null; + } + this.collectedScores = []; + return; + } + const rollMatch = text.match(/^(.+?) rolls (\d+) point\(s\)/); + if (rollMatch) { + const username = rollMatch[1].trim(); + const value = parseInt(rollMatch[2]); + console.log(`[Bancho] Roll: ${username} = ${value}`); + if (this.onRollResult) { + this.onRollResult(username, value); + } + return; + } + if (text.includes("All players are ready")) { + console.log("[Bancho] All players ready"); + if (this.allReadyResolve) { + this.allReadyResolve(); + this.allReadyResolve = null; + } + return; + } + } + parsePlayerMessage(sender, text) { + const pickMatch = text.match(/^!pick\s+([A-Z]{2})(\d*)/i); + if (pickMatch) { + const category = pickMatch[1].toUpperCase(); + const num = pickMatch[2] ? parseInt(pickMatch[2]) : 1; + const label = `${category}${num}`; + console.log(`[Bancho] Pick command from ${sender}: ${label}`); + if (this.onPickCommand) { + this.onPickCommand(sender, label); + } + return; + } + } + // ── IRC Commands ──────────────────────────────────────────────────── + async send(cmd) { + if (!this.channel) throw new Error("Lobby not created"); + console.log(`[Bancho] ${this.channelName} > ${cmd}`); + await this.channel.sendMessage(cmd); + } + async invite(username) { + await this.send(`!mp invite ${username.replace(/ /g, "_")}`); + } + async setProperties(teamMode, scoreMode, size) { + await this.send(`!mp set ${teamMode} ${scoreMode} ${size}`); + } + async setMap(beatmapId, playmode = 0) { + await this.send(`!mp map ${beatmapId} ${playmode}`); + } + async setMods(mods) { + const filtered = mods.filter((m) => !["NM", "TB", "None", "NF"].includes(m)); + if (mods.includes("FM") || mods.includes("Freemod")) { + await this.send("!mp mods Freemod NF"); + } else if (filtered.length > 0) { + await this.send(`!mp mods NF ${filtered.join(" ")}`); + } else { + await this.send("!mp mods NF"); + } + } + async startGame(delaySec = 5) { + await this.send(`!mp start ${delaySec}`); + } + async close() { + try { + await this.send("!mp close"); + } catch { + } + } + async chat(message) { + await this.send(message); + } + // ── Async Waiters ─────────────────────────────────────────────────── + /** + * Wait for all players to click "Ready" in osu! client. + * BanchoBot sends "All players are ready" when they do. + */ + waitForReady(timeoutMs = 18e4) { + return new Promise((resolve, reject) => { + const t = setTimeout(() => { + this.allReadyResolve = null; + reject(new Error("Timed out waiting for players to ready")); + }, timeoutMs); + this.allReadyResolve = () => { + clearTimeout(t); + resolve(); + }; + }); + } + /** + * Wait for "The match has finished!" and return all player scores. + */ + waitForScores(timeoutMs = 6e5) { + this.collectedScores = []; + return new Promise((resolve, reject) => { + const t = setTimeout(() => { + this.matchFinishedResolve = null; + reject(new Error("Timed out waiting for match scores")); + }, timeoutMs); + this.matchFinishedResolve = (scores) => { + clearTimeout(t); + resolve(scores); + }; + }); + } +} +const lobbies = /* @__PURE__ */ new Map(); +function getLobby(matchId) { + return lobbies.get(matchId); +} +function setLobby(matchId, lobby) { + lobbies.set(matchId, lobby); +} +function removeLobby(matchId) { + lobbies.delete(matchId); +} +function sleep(ms) { + return new Promise((r) => setTimeout(r, ms)); +} +async function initMatchLobby(matchId) { + const m = await getMatchFull(matchId); + const config = m.config; + const lobby = new TournamentLobby(); + const lobbyName = `VASH: (${m.participants[0]?.team.name}) vs (${m.participants[1]?.team.name})`; + console.log(`[Orchestrator] Creating lobby: ${lobbyName}`); + const { matchId: osuId, channel } = await lobby.create(lobbyName); + console.log(`[Orchestrator] Created ${channel} (osu mp/${osuId})`); + setLobby(matchId, lobby); + await db.update(match).set({ state: MATCH_STATES.LOBBY, osuLobbyId: osuId, startedAt: /* @__PURE__ */ new Date() }).where(eq(match.id, matchId)); + const scoreMode = config.scoringType === "score_v2" ? 3 : config.scoringType === "accuracy" ? 1 : config.scoringType === "combo" ? 2 : 0; + await lobby.setProperties(0, scoreMode, config.teamSize * 2); + await lobby.send("!mp password"); + await sleep(500); + const invited = /* @__PURE__ */ new Set(); + for (const p of m.participants) { + for (const pl of p.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name && !invited.has(u.name.toLowerCase())) { + await lobby.invite(u.name); + invited.add(u.name.toLowerCase()); + console.log(`[Orchestrator] Invited ${u.name}`); + await sleep(300); + } + } + } + setupChatHandlers(matchId, lobby); + await lobby.chat( + `Welcome! BO${config.bestOf}. Type !roll to decide pick order, or use the web UI.` + ); + await db.update(match).set({ state: MATCH_STATES.ROLLING }).where(eq(match.id, matchId)); + return { osuMatchId: osuId, channel }; +} +function setupChatHandlers(matchId, lobby) { + lobby.onRollResult = async (username, value) => { + try { + const m = await getMatchFull(matchId); + if (m.state !== MATCH_STATES.ROLLING) { + await lobby.chat(`Rolls are not active right now.`); + return; + } + let targetParticipant = null; + for (const p of m.participants) { + for (const pl of p.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name?.toLowerCase() === username.toLowerCase().replace(/_/g, " ")) { + if (p.rollValue === null) { + targetParticipant = p; + break; + } else if (!targetParticipant) { + const unrolled = m.participants.find((pp) => pp.rollValue === null); + if (unrolled) targetParticipant = unrolled; + } + } + } + if (targetParticipant?.rollValue === null) break; + } + if (!targetParticipant || targetParticipant.rollValue !== null) { + await lobby.chat(`${username}: already rolled or not in this match.`); + return; + } + await submitRoll(matchId, targetParticipant.id, value); + console.log(`[Orchestrator] IRC roll: ${username} = ${value}`); + const updated = await getMatchFull(matchId); + if (updated.state === MATCH_STATES.PICKING) { + const sorted = [...updated.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + await lobby.chat( + `Rolls complete! ${sorted[0]?.team.name} picks first. Use !pick (e.g. !pick NM1) or pick in web UI.` + ); + } + } catch (err) { + console.error("[Orchestrator] IRC roll error:", err.message); + } + }; + lobby.onPickCommand = async (username, slotLabel) => { + try { + const m = await getMatchFull(matchId); + if (m.state !== MATCH_STATES.PICKING) { + await lobby.chat(`Picks are not active right now.`); + return; + } + const labelMatch = slotLabel.match(/^([A-Z]{2})(\d+)$/); + if (!labelMatch) { + await lobby.chat(`${username}: Invalid slot. Use format like NM1, HD2, DT1.`); + return; + } + const category = labelMatch[1]; + const order = parseInt(labelMatch[2]); + const sorted = [...m.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + const expectedIdx = m.games.length % sorted.length; + const expectedPicker = sorted[expectedIdx]; + let isPickerTurn = false; + if (expectedPicker) { + for (const pl of expectedPicker.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name?.toLowerCase() === username.toLowerCase().replace(/_/g, " ")) { + isPickerTurn = true; + break; + } + } + } + if (!isPickerTurn && m.participants[0]?.teamId === m.participants[1]?.teamId) { + isPickerTurn = true; + } + if (!isPickerTurn) { + await lobby.chat(`${username}: It's not your turn to pick.`); + return; + } + const slot = m.mappool?.slots?.find( + (s) => s.category === category && s.orderInCategory === order + ); + if (!slot) { + await lobby.chat(`${username}: Slot ${slotLabel} not found in this mappool.`); + return; + } + const alreadyPlayed = m.games.some((g) => g.mappoolSlotId === slot.id); + if (alreadyPlayed) { + await lobby.chat(`${username}: ${slotLabel} has already been played.`); + return; + } + const game = await pickMap(matchId, expectedPicker.id, slot.id); + console.log(`[Orchestrator] IRC pick: ${username} picked ${slotLabel}`); + playPickedMap(matchId, game.id).catch( + (err) => console.error("[Orchestrator] IRC play failed:", err.message) + ); + } catch (err) { + console.error("[Orchestrator] IRC pick error:", err.message); + await lobby.chat(`Error: ${err.message}`).catch(() => { + }); + } + }; +} +async function playPickedMap(matchId, matchGameId) { + const lobby = getLobby(matchId); + if (!lobby) { + console.warn("[Orchestrator] No IRC lobby for", matchId, "— skipping IRC"); + return; + } + const game = await db.query.matchGame.findFirst({ + where: eq(matchGame.id, matchGameId), + with: { slot: true } + }); + if (!game) throw new Error("Game not found"); + const slot = game.slot; + await lobby.setMap(slot.beatmapId); + await sleep(1e3); + await lobby.setMods(slot.mods); + await sleep(500); + await lobby.chat( + `Playing ${slot.category}${slot.orderInCategory}. Ready up! (Game starts when all players are ready)` + ); + try { + await lobby.waitForReady(); + await lobby.chat("All ready — starting in 5s!"); + await lobby.startGame(5); + } catch (err) { + console.warn("[Orchestrator] Ready timeout:", err.message); + await lobby.chat("Ready timed out. Use the web UI to force start, or ready up!"); + return; + } + collectScores(matchId, matchGameId, lobby).catch( + (err) => console.error("[Orchestrator] Score collection failed:", err) + ); +} +async function forceStartGame(matchId) { + const lobby = getLobby(matchId); + if (!lobby) return; + await lobby.chat("Force starting in 10s!"); + await lobby.startGame(10); +} +async function collectScores(matchId, matchGameId, lobby) { + const ircScores = await lobby.waitForScores(); + console.log(`[Orchestrator] Scores for game ${matchGameId}:`, ircScores); + const m = await getMatchFull(matchId); + const scores = []; + for (const irc of ircScores) { + for (const participant of m.participants) { + for (const player of participant.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, player.userId) }); + const ircName = irc.username.toLowerCase().replace(/_/g, " "); + const dbName = u?.name?.toLowerCase(); + if (dbName && (dbName === ircName || dbName === irc.username.toLowerCase())) { + scores.push({ playerId: player.id, score: irc.score, passed: irc.passed }); + } + } + } + } + if (scores.length === 0) { + console.warn("[Orchestrator] No scores matched any IRC usernames"); + return; + } + await submitGameScores(matchGameId, scores); + const updated = await getMatchFull(matchId); + if (updated.state === MATCH_STATES.FINISHED) { + const winner = updated.participants.find((p) => p.teamId === updated.winnerId); + await lobby.chat(`GG! ${winner?.team.name ?? "?"} wins the match!`); + await sleep(5e3); + await lobby.close(); + removeLobby(matchId); + } else { + const sorted = [...updated.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + const nextIdx = updated.games.length % sorted.length; + const nextPicker = sorted[nextIdx]; + await lobby.chat( + `${nextPicker?.team.name}'s turn to pick. Use !pick (e.g. !pick HD1) or pick in web UI.` + ); + } +} +async function closeLobby(matchId) { + const lobby = getLobby(matchId); + if (lobby) { + await lobby.close(); + removeLobby(matchId); + } +} +export { + closeLobby as c, + forceStartGame as f, + getLobby as g, + initMatchLobby as i, + playPickedMap as p +}; diff --git a/.svelte-kit/adapter-node/chunks/query.js b/.svelte-kit/adapter-node/chunks/query.js new file mode 100644 index 0000000..a14a6fa --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/query.js @@ -0,0 +1,258 @@ +import { get_request_store, with_request_store } from "@sveltejs/kit/internal/server"; +import { z as stringify_remote_arg, f as create_remote_key, h as handle_error_and_jsonify } from "./shared.js"; +import { p as prerendering } from "./environment.js"; +import { parse } from "devalue"; +import { error } from "@sveltejs/kit"; +import { HttpError, SvelteKitError } from "@sveltejs/kit/internal"; +function create_validator(validate_or_fn, maybe_fn) { + if (!maybe_fn) { + return (arg) => { + if (arg !== void 0) { + error(400, "Bad Request"); + } + }; + } + if (validate_or_fn === "unchecked") { + return (arg) => arg; + } + if ("~standard" in validate_or_fn) { + return async (arg) => { + const { event, state } = get_request_store(); + const result = await validate_or_fn["~standard"].validate(arg); + if (result.issues) { + error( + 400, + await state.handleValidationError({ + issues: result.issues, + event + }) + ); + } + return result.value; + }; + } + throw new Error( + 'Invalid validator passed to remote function. Expected "unchecked" or a Standard Schema (https://standardschema.dev)' + ); +} +async function get_response(info, arg, state, get_result) { + await 0; + const cache = get_cache(info, state); + return cache[stringify_remote_arg(arg, state.transport)] ??= get_result(); +} +function parse_remote_response(data, transport) { + const revivers = {}; + for (const key in transport) { + revivers[key] = transport[key].decode; + } + return parse(data, revivers); +} +async function run_remote_function(event, state, allow_cookies, get_input, fn) { + const store = { + event: { + ...event, + setHeaders: () => { + throw new Error("setHeaders is not allowed in remote functions"); + }, + cookies: { + ...event.cookies, + set: (name, value, opts) => { + if (!allow_cookies) { + throw new Error("Cannot set cookies in `query` or `prerender` functions"); + } + if (opts.path && !opts.path.startsWith("/")) { + throw new Error("Cookies set in remote functions must have an absolute path"); + } + return event.cookies.set(name, value, opts); + }, + delete: (name, opts) => { + if (!allow_cookies) { + throw new Error("Cannot delete cookies in `query` or `prerender` functions"); + } + if (opts.path && !opts.path.startsWith("/")) { + throw new Error("Cookies deleted in remote functions must have an absolute path"); + } + return event.cookies.delete(name, opts); + } + } + }, + state: { + ...state, + is_in_remote_function: true + } + }; + const input = await with_request_store(store, get_input); + return with_request_store(store, () => fn(input)); +} +function get_cache(info, state = get_request_store().state) { + let cache = state.remote_data?.get(info); + if (cache === void 0) { + cache = {}; + (state.remote_data ??= /* @__PURE__ */ new Map()).set(info, cache); + } + return cache; +} +// @__NO_SIDE_EFFECTS__ +function query(validate_or_fn, maybe_fn) { + const fn = maybe_fn ?? validate_or_fn; + const validate = create_validator(validate_or_fn, maybe_fn); + const __ = { type: "query", id: "", name: "" }; + const wrapper = (arg) => { + if (prerendering) { + throw new Error( + `Cannot call query '${__.name}' while prerendering, as prerendered pages need static data. Use 'prerender' from $app/server instead` + ); + } + const { event, state } = get_request_store(); + const get_remote_function_result = () => run_remote_function(event, state, false, () => validate(arg), fn); + const promise = get_response(__, arg, state, get_remote_function_result); + promise.catch(() => { + }); + promise.set = (value) => update_refresh_value(get_refresh_context(__, "set", arg), value); + promise.refresh = () => { + const refresh_context = get_refresh_context(__, "refresh", arg); + const is_immediate_refresh = !refresh_context.cache[refresh_context.cache_key]; + const value = is_immediate_refresh ? promise : get_remote_function_result(); + return update_refresh_value(refresh_context, value, is_immediate_refresh); + }; + promise.withOverride = () => { + throw new Error(`Cannot call '${__.name}.withOverride()' on the server`); + }; + return ( + /** @type {RemoteQuery} */ + promise + ); + }; + Object.defineProperty(wrapper, "__", { value: __ }); + return wrapper; +} +// @__NO_SIDE_EFFECTS__ +function batch(validate_or_fn, maybe_fn) { + const fn = maybe_fn ?? validate_or_fn; + const validate = create_validator(validate_or_fn, maybe_fn); + const __ = { + type: "query_batch", + id: "", + name: "", + run: async (args, options) => { + const { event, state } = get_request_store(); + return run_remote_function( + event, + state, + false, + async () => Promise.all(args.map(validate)), + async (input) => { + const get_result = await fn(input); + return Promise.all( + input.map(async (arg, i) => { + try { + return { type: "result", data: get_result(arg, i) }; + } catch (error2) { + return { + type: "error", + error: await handle_error_and_jsonify(event, state, options, error2), + status: error2 instanceof HttpError || error2 instanceof SvelteKitError ? error2.status : 500 + }; + } + }) + ); + } + ); + } + }; + let batching = { args: [], resolvers: [] }; + const wrapper = (arg) => { + if (prerendering) { + throw new Error( + `Cannot call query.batch '${__.name}' while prerendering, as prerendered pages need static data. Use 'prerender' from $app/server instead` + ); + } + const { event, state } = get_request_store(); + const get_remote_function_result = () => { + return new Promise((resolve, reject) => { + batching.args.push(arg); + batching.resolvers.push({ resolve, reject }); + if (batching.args.length > 1) return; + setTimeout(async () => { + const batched = batching; + batching = { args: [], resolvers: [] }; + try { + return await run_remote_function( + event, + state, + false, + async () => Promise.all(batched.args.map(validate)), + async (input) => { + const get_result = await fn(input); + for (let i = 0; i < batched.resolvers.length; i++) { + try { + batched.resolvers[i].resolve(get_result(input[i], i)); + } catch (error2) { + batched.resolvers[i].reject(error2); + } + } + } + ); + } catch (error2) { + for (const resolver of batched.resolvers) { + resolver.reject(error2); + } + } + }, 0); + }); + }; + const promise = get_response(__, arg, state, get_remote_function_result); + promise.catch(() => { + }); + promise.set = (value) => update_refresh_value(get_refresh_context(__, "set", arg), value); + promise.refresh = () => { + const refresh_context = get_refresh_context(__, "refresh", arg); + const is_immediate_refresh = !refresh_context.cache[refresh_context.cache_key]; + const value = is_immediate_refresh ? promise : get_remote_function_result(); + return update_refresh_value(refresh_context, value, is_immediate_refresh); + }; + promise.withOverride = () => { + throw new Error(`Cannot call '${__.name}.withOverride()' on the server`); + }; + return ( + /** @type {RemoteQuery} */ + promise + ); + }; + Object.defineProperty(wrapper, "__", { value: __ }); + return wrapper; +} +Object.defineProperty(query, "batch", { value: batch, enumerable: true }); +function get_refresh_context(__, action, arg) { + const { state } = get_request_store(); + const { refreshes } = state; + if (!refreshes) { + const name = __.type === "query_batch" ? `query.batch '${__.name}'` : `query '${__.name}'`; + throw new Error( + `Cannot call ${action} on ${name} because it is not executed in the context of a command/form remote function` + ); + } + const cache = get_cache(__, state); + const cache_key = stringify_remote_arg(arg, state.transport); + const refreshes_key = create_remote_key(__.id, cache_key); + return { __, state, refreshes, refreshes_key, cache, cache_key }; +} +function update_refresh_value({ __, refreshes, refreshes_key, cache, cache_key }, value, is_immediate_refresh = false) { + const promise = Promise.resolve(value); + if (!is_immediate_refresh) { + cache[cache_key] = promise; + } + if (__.id) { + refreshes[refreshes_key] = promise; + } + return promise.then(() => { + }); +} +export { + get_response as a, + create_validator as c, + get_cache as g, + parse_remote_response as p, + query as q, + run_remote_function as r +}; diff --git a/.svelte-kit/adapter-node/chunks/root.js b/.svelte-kit/adapter-node/chunks/root.js new file mode 100644 index 0000000..c678a5c --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/root.js @@ -0,0 +1,2713 @@ +import { H as HYDRATION_ERROR, C as COMMENT_NODE, f as HYDRATION_END, h as HYDRATION_START, i as HYDRATION_START_ELSE, r as run_all, B as BOUNDARY_EFFECT, R as REACTION_RAN, E as ERROR_VALUE, j as EFFECT, k as CONNECTED, l as CLEAN, M as MAYBE_DIRTY, D as DIRTY, m as DERIVED, W as WAS_MARKED, I as INERT, o as BLOCK_EFFECT, U as UNINITIALIZED, p as deferred, q as BRANCH_EFFECT, t as ROOT_EFFECT, u as RENDER_EFFECT, v as MANAGED_EFFECT, w as HEAD_EFFECT, x as DESTROYED, A as ASYNC, y as includes, z as HYDRATION_START_FAILED, F as EFFECT_TRANSPARENT, G as EFFECT_PRESERVED, S as STALE_REACTION, n as noop, J as EAGER_EFFECT, K as STATE_SYMBOL, L as object_prototype, N as array_prototype, O as get_descriptor, P as get_prototype_of, Q as is_array, T as is_extensible, V as USER_EFFECT, X as REACTION_IS_UPDATING, Y as index_of, Z as define_property, _ as array_from, $ as is_passive_event, a0 as LEGACY_PROPS, a1 as render, a2 as setContext, d as derived } from "./index3.js"; +import { D as DEV } from "./false.js"; +function equals(value) { + return value === this.v; +} +function safe_not_equal(a, b) { + return a != a ? b == b : a !== b || a !== null && typeof a === "object" || typeof a === "function"; +} +function safe_equals(value) { + return !safe_not_equal(value, this.v); +} +function effect_update_depth_exceeded() { + { + throw new Error(`https://svelte.dev/e/effect_update_depth_exceeded`); + } +} +function hydration_failed() { + { + throw new Error(`https://svelte.dev/e/hydration_failed`); + } +} +function state_descriptors_fixed() { + { + throw new Error(`https://svelte.dev/e/state_descriptors_fixed`); + } +} +function state_prototype_fixed() { + { + throw new Error(`https://svelte.dev/e/state_prototype_fixed`); + } +} +function state_unsafe_mutation() { + { + throw new Error(`https://svelte.dev/e/state_unsafe_mutation`); + } +} +function svelte_boundary_reset_onerror() { + { + throw new Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`); + } +} +function hydration_mismatch(location) { + { + console.warn(`https://svelte.dev/e/hydration_mismatch`); + } +} +function svelte_boundary_reset_noop() { + { + console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`); + } +} +let hydrating = false; +function set_hydrating(value) { + hydrating = value; +} +let hydrate_node; +function set_hydrate_node(node) { + if (node === null) { + hydration_mismatch(); + throw HYDRATION_ERROR; + } + return hydrate_node = node; +} +function hydrate_next() { + return set_hydrate_node(/* @__PURE__ */ get_next_sibling(hydrate_node)); +} +function next(count = 1) { + if (hydrating) { + var i = count; + var node = hydrate_node; + while (i--) { + node = /** @type {TemplateNode} */ + /* @__PURE__ */ get_next_sibling(node); + } + hydrate_node = node; + } +} +function skip_nodes(remove = true) { + var depth = 0; + var node = hydrate_node; + while (true) { + if (node.nodeType === COMMENT_NODE) { + var data = ( + /** @type {Comment} */ + node.data + ); + if (data === HYDRATION_END) { + if (depth === 0) return node; + depth -= 1; + } else if (data === HYDRATION_START || data === HYDRATION_START_ELSE || // "[1", "[2", etc. for if blocks + data[0] === "[" && !isNaN(Number(data.slice(1)))) { + depth += 1; + } + } + var next2 = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ get_next_sibling(node) + ); + if (remove) node.remove(); + node = next2; + } +} +let tracing_mode_flag = false; +let component_context = null; +function set_component_context(context) { + component_context = context; +} +function push(props, runes = false, fn) { + component_context = { + p: component_context, + i: false, + c: null, + e: null, + s: props, + x: null, + l: null + }; +} +function pop(component) { + var context = ( + /** @type {ComponentContext} */ + component_context + ); + var effects = context.e; + if (effects !== null) { + context.e = null; + for (var fn of effects) { + create_user_effect(fn); + } + } + context.i = true; + component_context = context.p; + return ( + /** @type {T} */ + {} + ); +} +function is_runes() { + return true; +} +let micro_tasks = []; +function run_micro_tasks() { + var tasks = micro_tasks; + micro_tasks = []; + run_all(tasks); +} +function queue_micro_task(fn) { + if (micro_tasks.length === 0 && !is_flushing_sync) { + var tasks = micro_tasks; + queueMicrotask(() => { + if (tasks === micro_tasks) run_micro_tasks(); + }); + } + micro_tasks.push(fn); +} +function flush_tasks() { + while (micro_tasks.length > 0) { + run_micro_tasks(); + } +} +function handle_error(error) { + var effect = active_effect; + if (effect === null) { + active_reaction.f |= ERROR_VALUE; + return error; + } + if ((effect.f & REACTION_RAN) === 0 && (effect.f & EFFECT) === 0) { + throw error; + } + invoke_error_boundary(error, effect); +} +function invoke_error_boundary(error, effect) { + while (effect !== null) { + if ((effect.f & BOUNDARY_EFFECT) !== 0) { + if ((effect.f & REACTION_RAN) === 0) { + throw error; + } + try { + effect.b.error(error); + return; + } catch (e) { + error = e; + } + } + effect = effect.parent; + } + throw error; +} +const STATUS_MASK = -7169; +function set_signal_status(signal, status) { + signal.f = signal.f & STATUS_MASK | status; +} +function update_derived_status(derived2) { + if ((derived2.f & CONNECTED) !== 0 || derived2.deps === null) { + set_signal_status(derived2, CLEAN); + } else { + set_signal_status(derived2, MAYBE_DIRTY); + } +} +function clear_marked(deps) { + if (deps === null) return; + for (const dep of deps) { + if ((dep.f & DERIVED) === 0 || (dep.f & WAS_MARKED) === 0) { + continue; + } + dep.f ^= WAS_MARKED; + clear_marked( + /** @type {Derived} */ + dep.deps + ); + } +} +function defer_effect(effect, dirty_effects, maybe_dirty_effects) { + if ((effect.f & DIRTY) !== 0) { + dirty_effects.add(effect); + } else if ((effect.f & MAYBE_DIRTY) !== 0) { + maybe_dirty_effects.add(effect); + } + clear_marked(effect.deps); + set_signal_status(effect, CLEAN); +} +const batches = /* @__PURE__ */ new Set(); +let current_batch = null; +let batch_values = null; +let queued_root_effects = []; +let last_scheduled_effect = null; +let is_flushing = false; +let is_flushing_sync = false; +class Batch { + /** + * The current values of any sources that are updated in this batch + * They keys of this map are identical to `this.#previous` + * @type {Map} + */ + current = /* @__PURE__ */ new Map(); + /** + * The values of any sources that are updated in this batch _before_ those updates took place. + * They keys of this map are identical to `this.#current` + * @type {Map} + */ + previous = /* @__PURE__ */ new Map(); + /** + * When the batch is committed (and the DOM is updated), we need to remove old branches + * and append new ones by calling the functions added inside (if/each/key/etc) blocks + * @type {Set<() => void>} + */ + #commit_callbacks = /* @__PURE__ */ new Set(); + /** + * If a fork is discarded, we need to destroy any effects that are no longer needed + * @type {Set<(batch: Batch) => void>} + */ + #discard_callbacks = /* @__PURE__ */ new Set(); + /** + * The number of async effects that are currently in flight + */ + #pending = 0; + /** + * The number of async effects that are currently in flight, _not_ inside a pending boundary + */ + #blocking_pending = 0; + /** + * A deferred that resolves when the batch is committed, used with `settled()` + * TODO replace with Promise.withResolvers once supported widely enough + * @type {{ promise: Promise, resolve: (value?: any) => void, reject: (reason: unknown) => void } | null} + */ + #deferred = null; + /** + * Deferred effects (which run after async work has completed) that are DIRTY + * @type {Set} + */ + #dirty_effects = /* @__PURE__ */ new Set(); + /** + * Deferred effects that are MAYBE_DIRTY + * @type {Set} + */ + #maybe_dirty_effects = /* @__PURE__ */ new Set(); + /** + * A map of branches that still exist, but will be destroyed when this batch + * is committed — we skip over these during `process`. + * The value contains child effects that were dirty/maybe_dirty before being reset, + * so they can be rescheduled if the branch survives. + * @type {Map} + */ + #skipped_branches = /* @__PURE__ */ new Map(); + is_fork = false; + #decrement_queued = false; + #is_deferred() { + return this.is_fork || this.#blocking_pending > 0; + } + /** + * Add an effect to the #skipped_branches map and reset its children + * @param {Effect} effect + */ + skip_effect(effect) { + if (!this.#skipped_branches.has(effect)) { + this.#skipped_branches.set(effect, { d: [], m: [] }); + } + } + /** + * Remove an effect from the #skipped_branches map and reschedule + * any tracked dirty/maybe_dirty child effects + * @param {Effect} effect + */ + unskip_effect(effect) { + var tracked = this.#skipped_branches.get(effect); + if (tracked) { + this.#skipped_branches.delete(effect); + for (var e of tracked.d) { + set_signal_status(e, DIRTY); + schedule_effect(e); + } + for (e of tracked.m) { + set_signal_status(e, MAYBE_DIRTY); + schedule_effect(e); + } + } + } + /** + * + * @param {Effect[]} root_effects + */ + process(root_effects) { + queued_root_effects = []; + this.apply(); + var effects = []; + var render_effects = []; + for (const root2 of root_effects) { + this.#traverse_effect_tree(root2, effects, render_effects); + } + if (this.#is_deferred()) { + this.#defer_effects(render_effects); + this.#defer_effects(effects); + for (const [e, t] of this.#skipped_branches) { + reset_branch(e, t); + } + } else { + for (const fn of this.#commit_callbacks) fn(); + this.#commit_callbacks.clear(); + if (this.#pending === 0) { + this.#commit(); + } + current_batch = null; + flush_queued_effects(render_effects); + flush_queued_effects(effects); + this.#dirty_effects.clear(); + this.#maybe_dirty_effects.clear(); + this.#deferred?.resolve(); + } + batch_values = null; + } + /** + * Traverse the effect tree, executing effects or stashing + * them for later execution as appropriate + * @param {Effect} root + * @param {Effect[]} effects + * @param {Effect[]} render_effects + */ + #traverse_effect_tree(root2, effects, render_effects) { + root2.f ^= CLEAN; + var effect = root2.first; + while (effect !== null) { + var flags2 = effect.f; + var is_branch = (flags2 & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0; + var is_skippable_branch = is_branch && (flags2 & CLEAN) !== 0; + var skip = is_skippable_branch || (flags2 & INERT) !== 0 || this.#skipped_branches.has(effect); + if (!skip && effect.fn !== null) { + if (is_branch) { + effect.f ^= CLEAN; + } else if ((flags2 & EFFECT) !== 0) { + effects.push(effect); + } else if (is_dirty(effect)) { + if ((flags2 & BLOCK_EFFECT) !== 0) this.#maybe_dirty_effects.add(effect); + update_effect(effect); + } + var child = effect.first; + if (child !== null) { + effect = child; + continue; + } + } + while (effect !== null) { + var next2 = effect.next; + if (next2 !== null) { + effect = next2; + break; + } + effect = effect.parent; + } + } + } + /** + * @param {Effect[]} effects + */ + #defer_effects(effects) { + for (var i = 0; i < effects.length; i += 1) { + defer_effect(effects[i], this.#dirty_effects, this.#maybe_dirty_effects); + } + } + /** + * Associate a change to a given source with the current + * batch, noting its previous and current values + * @param {Source} source + * @param {any} value + */ + capture(source2, value) { + if (value !== UNINITIALIZED && !this.previous.has(source2)) { + this.previous.set(source2, value); + } + if ((source2.f & ERROR_VALUE) === 0) { + this.current.set(source2, source2.v); + batch_values?.set(source2, source2.v); + } + } + activate() { + current_batch = this; + this.apply(); + } + deactivate() { + if (current_batch !== this) return; + current_batch = null; + batch_values = null; + } + flush() { + this.activate(); + if (queued_root_effects.length > 0) { + flush_effects(); + if (current_batch !== null && current_batch !== this) { + return; + } + } else if (this.#pending === 0) { + this.process([]); + } + this.deactivate(); + } + discard() { + for (const fn of this.#discard_callbacks) fn(this); + this.#discard_callbacks.clear(); + } + #commit() { + if (batches.size > 1) { + this.previous.clear(); + var previous_batch_values = batch_values; + var is_earlier = true; + for (const batch of batches) { + if (batch === this) { + is_earlier = false; + continue; + } + const sources = []; + for (const [source2, value] of this.current) { + if (batch.current.has(source2)) { + if (is_earlier && value !== batch.current.get(source2)) { + batch.current.set(source2, value); + } else { + continue; + } + } + sources.push(source2); + } + if (sources.length === 0) { + continue; + } + const others = [...batch.current.keys()].filter((s) => !this.current.has(s)); + if (others.length > 0) { + var prev_queued_root_effects = queued_root_effects; + queued_root_effects = []; + const marked = /* @__PURE__ */ new Set(); + const checked = /* @__PURE__ */ new Map(); + for (const source2 of sources) { + mark_effects(source2, others, marked, checked); + } + if (queued_root_effects.length > 0) { + current_batch = batch; + batch.apply(); + for (const root2 of queued_root_effects) { + batch.#traverse_effect_tree(root2, [], []); + } + batch.deactivate(); + } + queued_root_effects = prev_queued_root_effects; + } + } + current_batch = null; + batch_values = previous_batch_values; + } + this.#skipped_branches.clear(); + batches.delete(this); + } + /** + * + * @param {boolean} blocking + */ + increment(blocking) { + this.#pending += 1; + if (blocking) this.#blocking_pending += 1; + } + /** + * + * @param {boolean} blocking + */ + decrement(blocking) { + this.#pending -= 1; + if (blocking) this.#blocking_pending -= 1; + if (this.#decrement_queued) return; + this.#decrement_queued = true; + queue_micro_task(() => { + this.#decrement_queued = false; + if (!this.#is_deferred()) { + this.revive(); + } else if (queued_root_effects.length > 0) { + this.flush(); + } + }); + } + revive() { + for (const e of this.#dirty_effects) { + this.#maybe_dirty_effects.delete(e); + set_signal_status(e, DIRTY); + schedule_effect(e); + } + for (const e of this.#maybe_dirty_effects) { + set_signal_status(e, MAYBE_DIRTY); + schedule_effect(e); + } + this.flush(); + } + /** @param {() => void} fn */ + oncommit(fn) { + this.#commit_callbacks.add(fn); + } + /** @param {(batch: Batch) => void} fn */ + ondiscard(fn) { + this.#discard_callbacks.add(fn); + } + settled() { + return (this.#deferred ??= deferred()).promise; + } + static ensure() { + if (current_batch === null) { + const batch = current_batch = new Batch(); + batches.add(current_batch); + if (!is_flushing_sync) { + queue_micro_task(() => { + if (current_batch !== batch) { + return; + } + batch.flush(); + }); + } + } + return current_batch; + } + apply() { + return; + } +} +function flushSync(fn) { + var was_flushing_sync = is_flushing_sync; + is_flushing_sync = true; + try { + var result; + if (fn) ; + while (true) { + flush_tasks(); + if (queued_root_effects.length === 0) { + current_batch?.flush(); + if (queued_root_effects.length === 0) { + last_scheduled_effect = null; + return ( + /** @type {T} */ + result + ); + } + } + flush_effects(); + } + } finally { + is_flushing_sync = was_flushing_sync; + } +} +function flush_effects() { + is_flushing = true; + var source_stacks = null; + try { + var flush_count = 0; + while (queued_root_effects.length > 0) { + var batch = Batch.ensure(); + if (flush_count++ > 1e3) { + var updates, entry; + if (DEV) ; + infinite_loop_guard(); + } + batch.process(queued_root_effects); + old_values.clear(); + if (DEV) ; + } + } finally { + queued_root_effects = []; + is_flushing = false; + last_scheduled_effect = null; + } +} +function infinite_loop_guard() { + try { + effect_update_depth_exceeded(); + } catch (error) { + invoke_error_boundary(error, last_scheduled_effect); + } +} +let eager_block_effects = null; +function flush_queued_effects(effects) { + var length = effects.length; + if (length === 0) return; + var i = 0; + while (i < length) { + var effect = effects[i++]; + if ((effect.f & (DESTROYED | INERT)) === 0 && is_dirty(effect)) { + eager_block_effects = /* @__PURE__ */ new Set(); + update_effect(effect); + if (effect.deps === null && effect.first === null && effect.nodes === null && effect.teardown === null && effect.ac === null) { + unlink_effect(effect); + } + if (eager_block_effects?.size > 0) { + old_values.clear(); + for (const e of eager_block_effects) { + if ((e.f & (DESTROYED | INERT)) !== 0) continue; + const ordered_effects = [e]; + let ancestor = e.parent; + while (ancestor !== null) { + if (eager_block_effects.has(ancestor)) { + eager_block_effects.delete(ancestor); + ordered_effects.push(ancestor); + } + ancestor = ancestor.parent; + } + for (let j = ordered_effects.length - 1; j >= 0; j--) { + const e2 = ordered_effects[j]; + if ((e2.f & (DESTROYED | INERT)) !== 0) continue; + update_effect(e2); + } + } + eager_block_effects.clear(); + } + } + } + eager_block_effects = null; +} +function mark_effects(value, sources, marked, checked) { + if (marked.has(value)) return; + marked.add(value); + if (value.reactions !== null) { + for (const reaction of value.reactions) { + const flags2 = reaction.f; + if ((flags2 & DERIVED) !== 0) { + mark_effects( + /** @type {Derived} */ + reaction, + sources, + marked, + checked + ); + } else if ((flags2 & (ASYNC | BLOCK_EFFECT)) !== 0 && (flags2 & DIRTY) === 0 && depends_on(reaction, sources, checked)) { + set_signal_status(reaction, DIRTY); + schedule_effect( + /** @type {Effect} */ + reaction + ); + } + } + } +} +function depends_on(reaction, sources, checked) { + const depends = checked.get(reaction); + if (depends !== void 0) return depends; + if (reaction.deps !== null) { + for (const dep of reaction.deps) { + if (includes.call(sources, dep)) { + return true; + } + if ((dep.f & DERIVED) !== 0 && depends_on( + /** @type {Derived} */ + dep, + sources, + checked + )) { + checked.set( + /** @type {Derived} */ + dep, + true + ); + return true; + } + } + } + checked.set(reaction, false); + return false; +} +function schedule_effect(signal) { + var effect = last_scheduled_effect = signal; + var boundary2 = effect.b; + if (boundary2?.is_pending && (signal.f & (EFFECT | RENDER_EFFECT | MANAGED_EFFECT)) !== 0 && (signal.f & REACTION_RAN) === 0) { + boundary2.defer_effect(signal); + return; + } + while (effect.parent !== null) { + effect = effect.parent; + var flags2 = effect.f; + if (is_flushing && effect === active_effect && (flags2 & BLOCK_EFFECT) !== 0 && (flags2 & HEAD_EFFECT) === 0 && (flags2 & REACTION_RAN) !== 0) { + return; + } + if ((flags2 & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) { + if ((flags2 & CLEAN) === 0) { + return; + } + effect.f ^= CLEAN; + } + } + queued_root_effects.push(effect); +} +function reset_branch(effect, tracked) { + if ((effect.f & BRANCH_EFFECT) !== 0 && (effect.f & CLEAN) !== 0) { + return; + } + if ((effect.f & DIRTY) !== 0) { + tracked.d.push(effect); + } else if ((effect.f & MAYBE_DIRTY) !== 0) { + tracked.m.push(effect); + } + set_signal_status(effect, CLEAN); + var e = effect.first; + while (e !== null) { + reset_branch(e, tracked); + e = e.next; + } +} +function createSubscriber(start) { + let subscribers = 0; + let version = source(0); + let stop; + return () => { + if (effect_tracking()) { + get(version); + render_effect(() => { + if (subscribers === 0) { + stop = untrack(() => start(() => increment(version))); + } + subscribers += 1; + return () => { + queue_micro_task(() => { + subscribers -= 1; + if (subscribers === 0) { + stop?.(); + stop = void 0; + increment(version); + } + }); + }; + }); + } + }; +} +var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED; +function boundary(node, props, children, transform_error) { + new Boundary(node, props, children, transform_error); +} +class Boundary { + /** @type {Boundary | null} */ + parent; + is_pending = false; + /** + * API-level transformError transform function. Transforms errors before they reach the `failed` snippet. + * Inherited from parent boundary, or defaults to identity. + * @type {(error: unknown) => unknown} + */ + transform_error; + /** @type {TemplateNode} */ + #anchor; + /** @type {TemplateNode | null} */ + #hydrate_open = hydrating ? hydrate_node : null; + /** @type {BoundaryProps} */ + #props; + /** @type {((anchor: Node) => void)} */ + #children; + /** @type {Effect} */ + #effect; + /** @type {Effect | null} */ + #main_effect = null; + /** @type {Effect | null} */ + #pending_effect = null; + /** @type {Effect | null} */ + #failed_effect = null; + /** @type {DocumentFragment | null} */ + #offscreen_fragment = null; + #local_pending_count = 0; + #pending_count = 0; + #pending_count_update_queued = false; + /** @type {Set} */ + #dirty_effects = /* @__PURE__ */ new Set(); + /** @type {Set} */ + #maybe_dirty_effects = /* @__PURE__ */ new Set(); + /** + * A source containing the number of pending async deriveds/expressions. + * Only created if `$effect.pending()` is used inside the boundary, + * otherwise updating the source results in needless `Batch.ensure()` + * calls followed by no-op flushes + * @type {Source | null} + */ + #effect_pending = null; + #effect_pending_subscriber = createSubscriber(() => { + this.#effect_pending = source(this.#local_pending_count); + return () => { + this.#effect_pending = null; + }; + }); + /** + * @param {TemplateNode} node + * @param {BoundaryProps} props + * @param {((anchor: Node) => void)} children + * @param {((error: unknown) => unknown) | undefined} [transform_error] + */ + constructor(node, props, children, transform_error) { + this.#anchor = node; + this.#props = props; + this.#children = (anchor) => { + var effect = ( + /** @type {Effect} */ + active_effect + ); + effect.b = this; + effect.f |= BOUNDARY_EFFECT; + children(anchor); + }; + this.parent = /** @type {Effect} */ + active_effect.b; + this.transform_error = transform_error ?? this.parent?.transform_error ?? ((e) => e); + this.#effect = block(() => { + if (hydrating) { + const comment = ( + /** @type {Comment} */ + this.#hydrate_open + ); + hydrate_next(); + const server_rendered_pending = comment.data === HYDRATION_START_ELSE; + const server_rendered_failed = comment.data.startsWith(HYDRATION_START_FAILED); + if (server_rendered_failed) { + const serialized_error = JSON.parse(comment.data.slice(HYDRATION_START_FAILED.length)); + this.#hydrate_failed_content(serialized_error); + } else if (server_rendered_pending) { + this.#hydrate_pending_content(); + } else { + this.#hydrate_resolved_content(); + } + } else { + this.#render(); + } + }, flags); + if (hydrating) { + this.#anchor = hydrate_node; + } + } + #hydrate_resolved_content() { + try { + this.#main_effect = branch(() => this.#children(this.#anchor)); + } catch (error) { + this.error(error); + } + } + /** + * @param {unknown} error The deserialized error from the server's hydration comment + */ + #hydrate_failed_content(error) { + const failed = this.#props.failed; + if (!failed) return; + this.#failed_effect = branch(() => { + failed( + this.#anchor, + () => error, + () => () => { + } + ); + }); + } + #hydrate_pending_content() { + const pending = this.#props.pending; + if (!pending) return; + this.is_pending = true; + this.#pending_effect = branch(() => pending(this.#anchor)); + queue_micro_task(() => { + var fragment = this.#offscreen_fragment = document.createDocumentFragment(); + var anchor = create_text(); + fragment.append(anchor); + this.#main_effect = this.#run(() => { + Batch.ensure(); + return branch(() => this.#children(anchor)); + }); + if (this.#pending_count === 0) { + this.#anchor.before(fragment); + this.#offscreen_fragment = null; + pause_effect( + /** @type {Effect} */ + this.#pending_effect, + () => { + this.#pending_effect = null; + } + ); + this.#resolve(); + } + }); + } + #render() { + try { + this.is_pending = this.has_pending_snippet(); + this.#pending_count = 0; + this.#local_pending_count = 0; + this.#main_effect = branch(() => { + this.#children(this.#anchor); + }); + if (this.#pending_count > 0) { + var fragment = this.#offscreen_fragment = document.createDocumentFragment(); + move_effect(this.#main_effect, fragment); + const pending = ( + /** @type {(anchor: Node) => void} */ + this.#props.pending + ); + this.#pending_effect = branch(() => pending(this.#anchor)); + } else { + this.#resolve(); + } + } catch (error) { + this.error(error); + } + } + #resolve() { + this.is_pending = false; + for (const e of this.#dirty_effects) { + set_signal_status(e, DIRTY); + schedule_effect(e); + } + for (const e of this.#maybe_dirty_effects) { + set_signal_status(e, MAYBE_DIRTY); + schedule_effect(e); + } + this.#dirty_effects.clear(); + this.#maybe_dirty_effects.clear(); + } + /** + * Defer an effect inside a pending boundary until the boundary resolves + * @param {Effect} effect + */ + defer_effect(effect) { + defer_effect(effect, this.#dirty_effects, this.#maybe_dirty_effects); + } + /** + * Returns `false` if the effect exists inside a boundary whose pending snippet is shown + * @returns {boolean} + */ + is_rendered() { + return !this.is_pending && (!this.parent || this.parent.is_rendered()); + } + has_pending_snippet() { + return !!this.#props.pending; + } + /** + * @template T + * @param {() => T} fn + */ + #run(fn) { + var previous_effect = active_effect; + var previous_reaction = active_reaction; + var previous_ctx = component_context; + set_active_effect(this.#effect); + set_active_reaction(this.#effect); + set_component_context(this.#effect.ctx); + try { + return fn(); + } catch (e) { + handle_error(e); + return null; + } finally { + set_active_effect(previous_effect); + set_active_reaction(previous_reaction); + set_component_context(previous_ctx); + } + } + /** + * Updates the pending count associated with the currently visible pending snippet, + * if any, such that we can replace the snippet with content once work is done + * @param {1 | -1} d + */ + #update_pending_count(d) { + if (!this.has_pending_snippet()) { + if (this.parent) { + this.parent.#update_pending_count(d); + } + return; + } + this.#pending_count += d; + if (this.#pending_count === 0) { + this.#resolve(); + if (this.#pending_effect) { + pause_effect(this.#pending_effect, () => { + this.#pending_effect = null; + }); + } + if (this.#offscreen_fragment) { + this.#anchor.before(this.#offscreen_fragment); + this.#offscreen_fragment = null; + } + } + } + /** + * Update the source that powers `$effect.pending()` inside this boundary, + * and controls when the current `pending` snippet (if any) is removed. + * Do not call from inside the class + * @param {1 | -1} d + */ + update_pending_count(d) { + this.#update_pending_count(d); + this.#local_pending_count += d; + if (!this.#effect_pending || this.#pending_count_update_queued) return; + this.#pending_count_update_queued = true; + queue_micro_task(() => { + this.#pending_count_update_queued = false; + if (this.#effect_pending) { + internal_set(this.#effect_pending, this.#local_pending_count); + } + }); + } + get_effect_pending() { + this.#effect_pending_subscriber(); + return get( + /** @type {Source} */ + this.#effect_pending + ); + } + /** @param {unknown} error */ + error(error) { + var onerror = this.#props.onerror; + let failed = this.#props.failed; + if (!onerror && !failed) { + throw error; + } + if (this.#main_effect) { + destroy_effect(this.#main_effect); + this.#main_effect = null; + } + if (this.#pending_effect) { + destroy_effect(this.#pending_effect); + this.#pending_effect = null; + } + if (this.#failed_effect) { + destroy_effect(this.#failed_effect); + this.#failed_effect = null; + } + if (hydrating) { + set_hydrate_node( + /** @type {TemplateNode} */ + this.#hydrate_open + ); + next(); + set_hydrate_node(skip_nodes()); + } + var did_reset = false; + var calling_on_error = false; + const reset = () => { + if (did_reset) { + svelte_boundary_reset_noop(); + return; + } + did_reset = true; + if (calling_on_error) { + svelte_boundary_reset_onerror(); + } + if (this.#failed_effect !== null) { + pause_effect(this.#failed_effect, () => { + this.#failed_effect = null; + }); + } + this.#run(() => { + Batch.ensure(); + this.#render(); + }); + }; + const handle_error_result = (transformed_error) => { + try { + calling_on_error = true; + onerror?.(transformed_error, reset); + calling_on_error = false; + } catch (error2) { + invoke_error_boundary(error2, this.#effect && this.#effect.parent); + } + if (failed) { + this.#failed_effect = this.#run(() => { + Batch.ensure(); + try { + return branch(() => { + var effect = ( + /** @type {Effect} */ + active_effect + ); + effect.b = this; + effect.f |= BOUNDARY_EFFECT; + failed( + this.#anchor, + () => transformed_error, + () => reset + ); + }); + } catch (error2) { + invoke_error_boundary( + error2, + /** @type {Effect} */ + this.#effect.parent + ); + return null; + } + }); + } + }; + queue_micro_task(() => { + var result; + try { + result = this.transform_error(error); + } catch (e) { + invoke_error_boundary(e, this.#effect && this.#effect.parent); + return; + } + if (result !== null && typeof result === "object" && typeof /** @type {any} */ + result.then === "function") { + result.then( + handle_error_result, + /** @param {unknown} e */ + (e) => invoke_error_boundary(e, this.#effect && this.#effect.parent) + ); + } else { + handle_error_result(result); + } + }); + } +} +function destroy_derived_effects(derived2) { + var effects = derived2.effects; + if (effects !== null) { + derived2.effects = null; + for (var i = 0; i < effects.length; i += 1) { + destroy_effect( + /** @type {Effect} */ + effects[i] + ); + } + } +} +function get_derived_parent_effect(derived2) { + var parent = derived2.parent; + while (parent !== null) { + if ((parent.f & DERIVED) === 0) { + return (parent.f & DESTROYED) === 0 ? ( + /** @type {Effect} */ + parent + ) : null; + } + parent = parent.parent; + } + return null; +} +function execute_derived(derived2) { + var value; + var prev_active_effect = active_effect; + set_active_effect(get_derived_parent_effect(derived2)); + { + try { + derived2.f &= ~WAS_MARKED; + destroy_derived_effects(derived2); + value = update_reaction(derived2); + } finally { + set_active_effect(prev_active_effect); + } + } + return value; +} +function update_derived(derived2) { + var value = execute_derived(derived2); + if (!derived2.equals(value)) { + derived2.wv = increment_write_version(); + if (!current_batch?.is_fork || derived2.deps === null) { + derived2.v = value; + if (derived2.deps === null) { + set_signal_status(derived2, CLEAN); + return; + } + } + } + if (is_destroying_effect) { + return; + } + if (batch_values !== null) { + if (effect_tracking() || current_batch?.is_fork) { + batch_values.set(derived2, value); + } + } else { + update_derived_status(derived2); + } +} +function freeze_derived_effects(derived2) { + if (derived2.effects === null) return; + for (const e of derived2.effects) { + if (e.teardown || e.ac) { + e.teardown?.(); + e.ac?.abort(STALE_REACTION); + e.teardown = noop; + e.ac = null; + remove_reactions(e, 0); + destroy_effect_children(e); + } + } +} +function unfreeze_derived_effects(derived2) { + if (derived2.effects === null) return; + for (const e of derived2.effects) { + if (e.teardown) { + update_effect(e); + } + } +} +let eager_effects = /* @__PURE__ */ new Set(); +const old_values = /* @__PURE__ */ new Map(); +let eager_effects_deferred = false; +function source(v, stack) { + var signal = { + f: 0, + // TODO ideally we could skip this altogether, but it causes type errors + v, + reactions: null, + equals, + rv: 0, + wv: 0 + }; + return signal; +} +// @__NO_SIDE_EFFECTS__ +function state(v, stack) { + const s = source(v); + push_reaction_value(s); + return s; +} +// @__NO_SIDE_EFFECTS__ +function mutable_source(initial_value, immutable = false, trackable = true) { + const s = source(initial_value); + if (!immutable) { + s.equals = safe_equals; + } + return s; +} +function set(source2, value, should_proxy = false) { + if (active_reaction !== null && // since we are untracking the function inside `$inspect.with` we need to add this check + // to ensure we error if state is set inside an inspect effect + (!untracking || (active_reaction.f & EAGER_EFFECT) !== 0) && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT | ASYNC | EAGER_EFFECT)) !== 0 && (current_sources === null || !includes.call(current_sources, source2))) { + state_unsafe_mutation(); + } + let new_value = should_proxy ? proxy(value) : value; + return internal_set(source2, new_value); +} +function internal_set(source2, value) { + if (!source2.equals(value)) { + var old_value = source2.v; + if (is_destroying_effect) { + old_values.set(source2, value); + } else { + old_values.set(source2, old_value); + } + source2.v = value; + var batch = Batch.ensure(); + batch.capture(source2, old_value); + if ((source2.f & DERIVED) !== 0) { + const derived2 = ( + /** @type {Derived} */ + source2 + ); + if ((source2.f & DIRTY) !== 0) { + execute_derived(derived2); + } + update_derived_status(derived2); + } + source2.wv = increment_write_version(); + mark_reactions(source2, DIRTY); + if (active_effect !== null && (active_effect.f & CLEAN) !== 0 && (active_effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0) { + if (untracked_writes === null) { + set_untracked_writes([source2]); + } else { + untracked_writes.push(source2); + } + } + if (!batch.is_fork && eager_effects.size > 0 && !eager_effects_deferred) { + flush_eager_effects(); + } + } + return value; +} +function flush_eager_effects() { + eager_effects_deferred = false; + for (const effect of eager_effects) { + if ((effect.f & CLEAN) !== 0) { + set_signal_status(effect, MAYBE_DIRTY); + } + if (is_dirty(effect)) { + update_effect(effect); + } + } + eager_effects.clear(); +} +function increment(source2) { + set(source2, source2.v + 1); +} +function mark_reactions(signal, status) { + var reactions = signal.reactions; + if (reactions === null) return; + var length = reactions.length; + for (var i = 0; i < length; i++) { + var reaction = reactions[i]; + var flags2 = reaction.f; + var not_dirty = (flags2 & DIRTY) === 0; + if (not_dirty) { + set_signal_status(reaction, status); + } + if ((flags2 & DERIVED) !== 0) { + var derived2 = ( + /** @type {Derived} */ + reaction + ); + batch_values?.delete(derived2); + if ((flags2 & WAS_MARKED) === 0) { + if (flags2 & CONNECTED) { + reaction.f |= WAS_MARKED; + } + mark_reactions(derived2, MAYBE_DIRTY); + } + } else if (not_dirty) { + if ((flags2 & BLOCK_EFFECT) !== 0 && eager_block_effects !== null) { + eager_block_effects.add( + /** @type {Effect} */ + reaction + ); + } + schedule_effect( + /** @type {Effect} */ + reaction + ); + } + } +} +function proxy(value) { + if (typeof value !== "object" || value === null || STATE_SYMBOL in value) { + return value; + } + const prototype = get_prototype_of(value); + if (prototype !== object_prototype && prototype !== array_prototype) { + return value; + } + var sources = /* @__PURE__ */ new Map(); + var is_proxied_array = is_array(value); + var version = /* @__PURE__ */ state(0); + var parent_version = update_version; + var with_parent = (fn) => { + if (update_version === parent_version) { + return fn(); + } + var reaction = active_reaction; + var version2 = update_version; + set_active_reaction(null); + set_update_version(parent_version); + var result = fn(); + set_active_reaction(reaction); + set_update_version(version2); + return result; + }; + if (is_proxied_array) { + sources.set("length", /* @__PURE__ */ state( + /** @type {any[]} */ + value.length + )); + } + return new Proxy( + /** @type {any} */ + value, + { + defineProperty(_, prop, descriptor) { + if (!("value" in descriptor) || descriptor.configurable === false || descriptor.enumerable === false || descriptor.writable === false) { + state_descriptors_fixed(); + } + var s = sources.get(prop); + if (s === void 0) { + with_parent(() => { + var s2 = /* @__PURE__ */ state(descriptor.value); + sources.set(prop, s2); + return s2; + }); + } else { + set(s, descriptor.value, true); + } + return true; + }, + deleteProperty(target, prop) { + var s = sources.get(prop); + if (s === void 0) { + if (prop in target) { + const s2 = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED)); + sources.set(prop, s2); + increment(version); + } + } else { + set(s, UNINITIALIZED); + increment(version); + } + return true; + }, + get(target, prop, receiver) { + if (prop === STATE_SYMBOL) { + return value; + } + var s = sources.get(prop); + var exists = prop in target; + if (s === void 0 && (!exists || get_descriptor(target, prop)?.writable)) { + s = with_parent(() => { + var p = proxy(exists ? target[prop] : UNINITIALIZED); + var s2 = /* @__PURE__ */ state(p); + return s2; + }); + sources.set(prop, s); + } + if (s !== void 0) { + var v = get(s); + return v === UNINITIALIZED ? void 0 : v; + } + return Reflect.get(target, prop, receiver); + }, + getOwnPropertyDescriptor(target, prop) { + var descriptor = Reflect.getOwnPropertyDescriptor(target, prop); + if (descriptor && "value" in descriptor) { + var s = sources.get(prop); + if (s) descriptor.value = get(s); + } else if (descriptor === void 0) { + var source2 = sources.get(prop); + var value2 = source2?.v; + if (source2 !== void 0 && value2 !== UNINITIALIZED) { + return { + enumerable: true, + configurable: true, + value: value2, + writable: true + }; + } + } + return descriptor; + }, + has(target, prop) { + if (prop === STATE_SYMBOL) { + return true; + } + var s = sources.get(prop); + var has = s !== void 0 && s.v !== UNINITIALIZED || Reflect.has(target, prop); + if (s !== void 0 || active_effect !== null && (!has || get_descriptor(target, prop)?.writable)) { + if (s === void 0) { + s = with_parent(() => { + var p = has ? proxy(target[prop]) : UNINITIALIZED; + var s2 = /* @__PURE__ */ state(p); + return s2; + }); + sources.set(prop, s); + } + var value2 = get(s); + if (value2 === UNINITIALIZED) { + return false; + } + } + return has; + }, + set(target, prop, value2, receiver) { + var s = sources.get(prop); + var has = prop in target; + if (is_proxied_array && prop === "length") { + for (var i = value2; i < /** @type {Source} */ + s.v; i += 1) { + var other_s = sources.get(i + ""); + if (other_s !== void 0) { + set(other_s, UNINITIALIZED); + } else if (i in target) { + other_s = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED)); + sources.set(i + "", other_s); + } + } + } + if (s === void 0) { + if (!has || get_descriptor(target, prop)?.writable) { + s = with_parent(() => /* @__PURE__ */ state(void 0)); + set(s, proxy(value2)); + sources.set(prop, s); + } + } else { + has = s.v !== UNINITIALIZED; + var p = with_parent(() => proxy(value2)); + set(s, p); + } + var descriptor = Reflect.getOwnPropertyDescriptor(target, prop); + if (descriptor?.set) { + descriptor.set.call(receiver, value2); + } + if (!has) { + if (is_proxied_array && typeof prop === "string") { + var ls = ( + /** @type {Source} */ + sources.get("length") + ); + var n = Number(prop); + if (Number.isInteger(n) && n >= ls.v) { + set(ls, n + 1); + } + } + increment(version); + } + return true; + }, + ownKeys(target) { + get(version); + var own_keys = Reflect.ownKeys(target).filter((key2) => { + var source3 = sources.get(key2); + return source3 === void 0 || source3.v !== UNINITIALIZED; + }); + for (var [key, source2] of sources) { + if (source2.v !== UNINITIALIZED && !(key in target)) { + own_keys.push(key); + } + } + return own_keys; + }, + setPrototypeOf() { + state_prototype_fixed(); + } + } + ); +} +var $window; +var first_child_getter; +var next_sibling_getter; +function init_operations() { + if ($window !== void 0) { + return; + } + $window = window; + var element_prototype = Element.prototype; + var node_prototype = Node.prototype; + var text_prototype = Text.prototype; + first_child_getter = get_descriptor(node_prototype, "firstChild").get; + next_sibling_getter = get_descriptor(node_prototype, "nextSibling").get; + if (is_extensible(element_prototype)) { + element_prototype.__click = void 0; + element_prototype.__className = void 0; + element_prototype.__attributes = null; + element_prototype.__style = void 0; + element_prototype.__e = void 0; + } + if (is_extensible(text_prototype)) { + text_prototype.__t = void 0; + } +} +function create_text(value = "") { + return document.createTextNode(value); +} +// @__NO_SIDE_EFFECTS__ +function get_first_child(node) { + return ( + /** @type {TemplateNode | null} */ + first_child_getter.call(node) + ); +} +// @__NO_SIDE_EFFECTS__ +function get_next_sibling(node) { + return ( + /** @type {TemplateNode | null} */ + next_sibling_getter.call(node) + ); +} +function clear_text_content(node) { + node.textContent = ""; +} +function without_reactive_context(fn) { + var previous_reaction = active_reaction; + var previous_effect = active_effect; + set_active_reaction(null); + set_active_effect(null); + try { + return fn(); + } finally { + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); + } +} +function push_effect(effect, parent_effect) { + var parent_last = parent_effect.last; + if (parent_last === null) { + parent_effect.last = parent_effect.first = effect; + } else { + parent_last.next = effect; + effect.prev = parent_last; + parent_effect.last = effect; + } +} +function create_effect(type, fn, sync) { + var parent = active_effect; + if (parent !== null && (parent.f & INERT) !== 0) { + type |= INERT; + } + var effect = { + ctx: component_context, + deps: null, + nodes: null, + f: type | DIRTY | CONNECTED, + first: null, + fn, + last: null, + next: null, + parent, + b: parent && parent.b, + prev: null, + teardown: null, + wv: 0, + ac: null + }; + if (sync) { + try { + update_effect(effect); + } catch (e2) { + destroy_effect(effect); + throw e2; + } + } else if (fn !== null) { + schedule_effect(effect); + } + var e = effect; + if (sync && e.deps === null && e.teardown === null && e.nodes === null && e.first === e.last && // either `null`, or a singular child + (e.f & EFFECT_PRESERVED) === 0) { + e = e.first; + if ((type & BLOCK_EFFECT) !== 0 && (type & EFFECT_TRANSPARENT) !== 0 && e !== null) { + e.f |= EFFECT_TRANSPARENT; + } + } + if (e !== null) { + e.parent = parent; + if (parent !== null) { + push_effect(e, parent); + } + if (active_reaction !== null && (active_reaction.f & DERIVED) !== 0 && (type & ROOT_EFFECT) === 0) { + var derived2 = ( + /** @type {Derived} */ + active_reaction + ); + (derived2.effects ??= []).push(e); + } + } + return effect; +} +function effect_tracking() { + return active_reaction !== null && !untracking; +} +function create_user_effect(fn) { + return create_effect(EFFECT | USER_EFFECT, fn, false); +} +function component_root(fn) { + Batch.ensure(); + const effect = create_effect(ROOT_EFFECT | EFFECT_PRESERVED, fn, true); + return (options = {}) => { + return new Promise((fulfil) => { + if (options.outro) { + pause_effect(effect, () => { + destroy_effect(effect); + fulfil(void 0); + }); + } else { + destroy_effect(effect); + fulfil(void 0); + } + }); + }; +} +function render_effect(fn, flags2 = 0) { + return create_effect(RENDER_EFFECT | flags2, fn, true); +} +function block(fn, flags2 = 0) { + var effect = create_effect(BLOCK_EFFECT | flags2, fn, true); + return effect; +} +function branch(fn) { + return create_effect(BRANCH_EFFECT | EFFECT_PRESERVED, fn, true); +} +function execute_effect_teardown(effect) { + var teardown = effect.teardown; + if (teardown !== null) { + const previously_destroying_effect = is_destroying_effect; + const previous_reaction = active_reaction; + set_is_destroying_effect(true); + set_active_reaction(null); + try { + teardown.call(null); + } finally { + set_is_destroying_effect(previously_destroying_effect); + set_active_reaction(previous_reaction); + } + } +} +function destroy_effect_children(signal, remove_dom = false) { + var effect = signal.first; + signal.first = signal.last = null; + while (effect !== null) { + const controller = effect.ac; + if (controller !== null) { + without_reactive_context(() => { + controller.abort(STALE_REACTION); + }); + } + var next2 = effect.next; + if ((effect.f & ROOT_EFFECT) !== 0) { + effect.parent = null; + } else { + destroy_effect(effect, remove_dom); + } + effect = next2; + } +} +function destroy_block_effect_children(signal) { + var effect = signal.first; + while (effect !== null) { + var next2 = effect.next; + if ((effect.f & BRANCH_EFFECT) === 0) { + destroy_effect(effect); + } + effect = next2; + } +} +function destroy_effect(effect, remove_dom = true) { + var removed = false; + if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes !== null && effect.nodes.end !== null) { + remove_effect_dom( + effect.nodes.start, + /** @type {TemplateNode} */ + effect.nodes.end + ); + removed = true; + } + destroy_effect_children(effect, remove_dom && !removed); + remove_reactions(effect, 0); + set_signal_status(effect, DESTROYED); + var transitions = effect.nodes && effect.nodes.t; + if (transitions !== null) { + for (const transition of transitions) { + transition.stop(); + } + } + execute_effect_teardown(effect); + var parent = effect.parent; + if (parent !== null && parent.first !== null) { + unlink_effect(effect); + } + effect.next = effect.prev = effect.teardown = effect.ctx = effect.deps = effect.fn = effect.nodes = effect.ac = null; +} +function remove_effect_dom(node, end) { + while (node !== null) { + var next2 = node === end ? null : /* @__PURE__ */ get_next_sibling(node); + node.remove(); + node = next2; + } +} +function unlink_effect(effect) { + var parent = effect.parent; + var prev = effect.prev; + var next2 = effect.next; + if (prev !== null) prev.next = next2; + if (next2 !== null) next2.prev = prev; + if (parent !== null) { + if (parent.first === effect) parent.first = next2; + if (parent.last === effect) parent.last = prev; + } +} +function pause_effect(effect, callback, destroy = true) { + var transitions = []; + pause_children(effect, transitions, true); + var fn = () => { + if (destroy) destroy_effect(effect); + if (callback) callback(); + }; + var remaining = transitions.length; + if (remaining > 0) { + var check = () => --remaining || fn(); + for (var transition of transitions) { + transition.out(check); + } + } else { + fn(); + } +} +function pause_children(effect, transitions, local) { + if ((effect.f & INERT) !== 0) return; + effect.f ^= INERT; + var t = effect.nodes && effect.nodes.t; + if (t !== null) { + for (const transition of t) { + if (transition.is_global || local) { + transitions.push(transition); + } + } + } + var child = effect.first; + while (child !== null) { + var sibling = child.next; + var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || // If this is a branch effect without a block effect parent, + // it means the parent block effect was pruned. In that case, + // transparency information was transferred to the branch effect. + (child.f & BRANCH_EFFECT) !== 0 && (effect.f & BLOCK_EFFECT) !== 0; + pause_children(child, transitions, transparent ? local : false); + child = sibling; + } +} +function move_effect(effect, fragment) { + if (!effect.nodes) return; + var node = effect.nodes.start; + var end = effect.nodes.end; + while (node !== null) { + var next2 = node === end ? null : /* @__PURE__ */ get_next_sibling(node); + fragment.append(node); + node = next2; + } +} +let is_updating_effect = false; +let is_destroying_effect = false; +function set_is_destroying_effect(value) { + is_destroying_effect = value; +} +let active_reaction = null; +let untracking = false; +function set_active_reaction(reaction) { + active_reaction = reaction; +} +let active_effect = null; +function set_active_effect(effect) { + active_effect = effect; +} +let current_sources = null; +function push_reaction_value(value) { + if (active_reaction !== null && true) { + if (current_sources === null) { + current_sources = [value]; + } else { + current_sources.push(value); + } + } +} +let new_deps = null; +let skipped_deps = 0; +let untracked_writes = null; +function set_untracked_writes(value) { + untracked_writes = value; +} +let write_version = 1; +let read_version = 0; +let update_version = read_version; +function set_update_version(value) { + update_version = value; +} +function increment_write_version() { + return ++write_version; +} +function is_dirty(reaction) { + var flags2 = reaction.f; + if ((flags2 & DIRTY) !== 0) { + return true; + } + if (flags2 & DERIVED) { + reaction.f &= ~WAS_MARKED; + } + if ((flags2 & MAYBE_DIRTY) !== 0) { + var dependencies = ( + /** @type {Value[]} */ + reaction.deps + ); + var length = dependencies.length; + for (var i = 0; i < length; i++) { + var dependency = dependencies[i]; + if (is_dirty( + /** @type {Derived} */ + dependency + )) { + update_derived( + /** @type {Derived} */ + dependency + ); + } + if (dependency.wv > reaction.wv) { + return true; + } + } + if ((flags2 & CONNECTED) !== 0 && // During time traveling we don't want to reset the status so that + // traversal of the graph in the other batches still happens + batch_values === null) { + set_signal_status(reaction, CLEAN); + } + } + return false; +} +function schedule_possible_effect_self_invalidation(signal, effect, root2 = true) { + var reactions = signal.reactions; + if (reactions === null) return; + if (current_sources !== null && includes.call(current_sources, signal)) { + return; + } + for (var i = 0; i < reactions.length; i++) { + var reaction = reactions[i]; + if ((reaction.f & DERIVED) !== 0) { + schedule_possible_effect_self_invalidation( + /** @type {Derived} */ + reaction, + effect, + false + ); + } else if (effect === reaction) { + if (root2) { + set_signal_status(reaction, DIRTY); + } else if ((reaction.f & CLEAN) !== 0) { + set_signal_status(reaction, MAYBE_DIRTY); + } + schedule_effect( + /** @type {Effect} */ + reaction + ); + } + } +} +function update_reaction(reaction) { + var previous_deps = new_deps; + var previous_skipped_deps = skipped_deps; + var previous_untracked_writes = untracked_writes; + var previous_reaction = active_reaction; + var previous_sources = current_sources; + var previous_component_context = component_context; + var previous_untracking = untracking; + var previous_update_version = update_version; + var flags2 = reaction.f; + new_deps = /** @type {null | Value[]} */ + null; + skipped_deps = 0; + untracked_writes = null; + active_reaction = (flags2 & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; + current_sources = null; + set_component_context(reaction.ctx); + untracking = false; + update_version = ++read_version; + if (reaction.ac !== null) { + without_reactive_context(() => { + reaction.ac.abort(STALE_REACTION); + }); + reaction.ac = null; + } + try { + reaction.f |= REACTION_IS_UPDATING; + var fn = ( + /** @type {Function} */ + reaction.fn + ); + var result = fn(); + reaction.f |= REACTION_RAN; + var deps = reaction.deps; + var is_fork = current_batch?.is_fork; + if (new_deps !== null) { + var i; + if (!is_fork) { + remove_reactions(reaction, skipped_deps); + } + if (deps !== null && skipped_deps > 0) { + deps.length = skipped_deps + new_deps.length; + for (i = 0; i < new_deps.length; i++) { + deps[skipped_deps + i] = new_deps[i]; + } + } else { + reaction.deps = deps = new_deps; + } + if (effect_tracking() && (reaction.f & CONNECTED) !== 0) { + for (i = skipped_deps; i < deps.length; i++) { + (deps[i].reactions ??= []).push(reaction); + } + } + } else if (!is_fork && deps !== null && skipped_deps < deps.length) { + remove_reactions(reaction, skipped_deps); + deps.length = skipped_deps; + } + if (is_runes() && untracked_writes !== null && !untracking && deps !== null && (reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0) { + for (i = 0; i < /** @type {Source[]} */ + untracked_writes.length; i++) { + schedule_possible_effect_self_invalidation( + untracked_writes[i], + /** @type {Effect} */ + reaction + ); + } + } + if (previous_reaction !== null && previous_reaction !== reaction) { + read_version++; + if (previous_reaction.deps !== null) { + for (let i2 = 0; i2 < previous_skipped_deps; i2 += 1) { + previous_reaction.deps[i2].rv = read_version; + } + } + if (previous_deps !== null) { + for (const dep of previous_deps) { + dep.rv = read_version; + } + } + if (untracked_writes !== null) { + if (previous_untracked_writes === null) { + previous_untracked_writes = untracked_writes; + } else { + previous_untracked_writes.push(.../** @type {Source[]} */ + untracked_writes); + } + } + } + if ((reaction.f & ERROR_VALUE) !== 0) { + reaction.f ^= ERROR_VALUE; + } + return result; + } catch (error) { + return handle_error(error); + } finally { + reaction.f ^= REACTION_IS_UPDATING; + new_deps = previous_deps; + skipped_deps = previous_skipped_deps; + untracked_writes = previous_untracked_writes; + active_reaction = previous_reaction; + current_sources = previous_sources; + set_component_context(previous_component_context); + untracking = previous_untracking; + update_version = previous_update_version; + } +} +function remove_reaction(signal, dependency) { + let reactions = dependency.reactions; + if (reactions !== null) { + var index = index_of.call(reactions, signal); + if (index !== -1) { + var new_length = reactions.length - 1; + if (new_length === 0) { + reactions = dependency.reactions = null; + } else { + reactions[index] = reactions[new_length]; + reactions.pop(); + } + } + } + if (reactions === null && (dependency.f & DERIVED) !== 0 && // Destroying a child effect while updating a parent effect can cause a dependency to appear + // to be unused, when in fact it is used by the currently-updating parent. Checking `new_deps` + // allows us to skip the expensive work of disconnecting and immediately reconnecting it + (new_deps === null || !includes.call(new_deps, dependency))) { + var derived2 = ( + /** @type {Derived} */ + dependency + ); + if ((derived2.f & CONNECTED) !== 0) { + derived2.f ^= CONNECTED; + derived2.f &= ~WAS_MARKED; + } + update_derived_status(derived2); + freeze_derived_effects(derived2); + remove_reactions(derived2, 0); + } +} +function remove_reactions(signal, start_index) { + var dependencies = signal.deps; + if (dependencies === null) return; + for (var i = start_index; i < dependencies.length; i++) { + remove_reaction(signal, dependencies[i]); + } +} +function update_effect(effect) { + var flags2 = effect.f; + if ((flags2 & DESTROYED) !== 0) { + return; + } + set_signal_status(effect, CLEAN); + var previous_effect = active_effect; + var was_updating_effect = is_updating_effect; + active_effect = effect; + is_updating_effect = true; + try { + if ((flags2 & (BLOCK_EFFECT | MANAGED_EFFECT)) !== 0) { + destroy_block_effect_children(effect); + } else { + destroy_effect_children(effect); + } + execute_effect_teardown(effect); + var teardown = update_reaction(effect); + effect.teardown = typeof teardown === "function" ? teardown : null; + effect.wv = write_version; + var dep; + if (DEV && tracing_mode_flag && (effect.f & DIRTY) !== 0 && effect.deps !== null) ; + } finally { + is_updating_effect = was_updating_effect; + active_effect = previous_effect; + } +} +function get(signal) { + var flags2 = signal.f; + var is_derived = (flags2 & DERIVED) !== 0; + if (active_reaction !== null && !untracking) { + var destroyed = active_effect !== null && (active_effect.f & DESTROYED) !== 0; + if (!destroyed && (current_sources === null || !includes.call(current_sources, signal))) { + var deps = active_reaction.deps; + if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) { + if (signal.rv < read_version) { + signal.rv = read_version; + if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { + skipped_deps++; + } else if (new_deps === null) { + new_deps = [signal]; + } else { + new_deps.push(signal); + } + } + } else { + (active_reaction.deps ??= []).push(signal); + var reactions = signal.reactions; + if (reactions === null) { + signal.reactions = [active_reaction]; + } else if (!includes.call(reactions, active_reaction)) { + reactions.push(active_reaction); + } + } + } + } + if (is_destroying_effect && old_values.has(signal)) { + return old_values.get(signal); + } + if (is_derived) { + var derived2 = ( + /** @type {Derived} */ + signal + ); + if (is_destroying_effect) { + var value = derived2.v; + if ((derived2.f & CLEAN) === 0 && derived2.reactions !== null || depends_on_old_values(derived2)) { + value = execute_derived(derived2); + } + old_values.set(derived2, value); + return value; + } + var should_connect = (derived2.f & CONNECTED) === 0 && !untracking && active_reaction !== null && (is_updating_effect || (active_reaction.f & CONNECTED) !== 0); + var is_new = (derived2.f & REACTION_RAN) === 0; + if (is_dirty(derived2)) { + if (should_connect) { + derived2.f |= CONNECTED; + } + update_derived(derived2); + } + if (should_connect && !is_new) { + unfreeze_derived_effects(derived2); + reconnect(derived2); + } + } + if (batch_values?.has(signal)) { + return batch_values.get(signal); + } + if ((signal.f & ERROR_VALUE) !== 0) { + throw signal.v; + } + return signal.v; +} +function reconnect(derived2) { + derived2.f |= CONNECTED; + if (derived2.deps === null) return; + for (const dep of derived2.deps) { + (dep.reactions ??= []).push(derived2); + if ((dep.f & DERIVED) !== 0 && (dep.f & CONNECTED) === 0) { + unfreeze_derived_effects( + /** @type {Derived} */ + dep + ); + reconnect( + /** @type {Derived} */ + dep + ); + } + } +} +function depends_on_old_values(derived2) { + if (derived2.v === UNINITIALIZED) return true; + if (derived2.deps === null) return false; + for (const dep of derived2.deps) { + if (old_values.has(dep)) { + return true; + } + if ((dep.f & DERIVED) !== 0 && depends_on_old_values( + /** @type {Derived} */ + dep + )) { + return true; + } + } + return false; +} +function untrack(fn) { + var previous_untracking = untracking; + try { + untracking = true; + return fn(); + } finally { + untracking = previous_untracking; + } +} +const event_symbol = /* @__PURE__ */ Symbol("events"); +const all_registered_events = /* @__PURE__ */ new Set(); +const root_event_handles = /* @__PURE__ */ new Set(); +let last_propagated_event = null; +function handle_event_propagation(event) { + var handler_element = this; + var owner_document = ( + /** @type {Node} */ + handler_element.ownerDocument + ); + var event_name = event.type; + var path = event.composedPath?.() || []; + var current_target = ( + /** @type {null | Element} */ + path[0] || event.target + ); + last_propagated_event = event; + var path_idx = 0; + var handled_at = last_propagated_event === event && event[event_symbol]; + if (handled_at) { + var at_idx = path.indexOf(handled_at); + if (at_idx !== -1 && (handler_element === document || handler_element === /** @type {any} */ + window)) { + event[event_symbol] = handler_element; + return; + } + var handler_idx = path.indexOf(handler_element); + if (handler_idx === -1) { + return; + } + if (at_idx <= handler_idx) { + path_idx = at_idx; + } + } + current_target = /** @type {Element} */ + path[path_idx] || event.target; + if (current_target === handler_element) return; + define_property(event, "currentTarget", { + configurable: true, + get() { + return current_target || owner_document; + } + }); + var previous_reaction = active_reaction; + var previous_effect = active_effect; + set_active_reaction(null); + set_active_effect(null); + try { + var throw_error; + var other_errors = []; + while (current_target !== null) { + var parent_element = current_target.assignedSlot || current_target.parentNode || /** @type {any} */ + current_target.host || null; + try { + var delegated = current_target[event_symbol]?.[event_name]; + if (delegated != null && (!/** @type {any} */ + current_target.disabled || // DOM could've been updated already by the time this is reached, so we check this as well + // -> the target could not have been disabled because it emits the event in the first place + event.target === current_target)) { + delegated.call(current_target, event); + } + } catch (error) { + if (throw_error) { + other_errors.push(error); + } else { + throw_error = error; + } + } + if (event.cancelBubble || parent_element === handler_element || parent_element === null) { + break; + } + current_target = parent_element; + } + if (throw_error) { + for (let error of other_errors) { + queueMicrotask(() => { + throw error; + }); + } + throw throw_error; + } + } finally { + event[event_symbol] = handler_element; + delete event.currentTarget; + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); + } +} +function assign_nodes(start, end) { + var effect = ( + /** @type {Effect} */ + active_effect + ); + if (effect.nodes === null) { + effect.nodes = { start, end, a: null, t: null }; + } +} +function mount(component, options) { + return _mount(component, options); +} +function hydrate(component, options) { + init_operations(); + options.intro = options.intro ?? false; + const target = options.target; + const was_hydrating = hydrating; + const previous_hydrate_node = hydrate_node; + try { + var anchor = /* @__PURE__ */ get_first_child(target); + while (anchor && (anchor.nodeType !== COMMENT_NODE || /** @type {Comment} */ + anchor.data !== HYDRATION_START)) { + anchor = /* @__PURE__ */ get_next_sibling(anchor); + } + if (!anchor) { + throw HYDRATION_ERROR; + } + set_hydrating(true); + set_hydrate_node( + /** @type {Comment} */ + anchor + ); + const instance = _mount(component, { ...options, anchor }); + set_hydrating(false); + return ( + /** @type {Exports} */ + instance + ); + } catch (error) { + if (error instanceof Error && error.message.split("\n").some((line) => line.startsWith("https://svelte.dev/e/"))) { + throw error; + } + if (error !== HYDRATION_ERROR) { + console.warn("Failed to hydrate: ", error); + } + if (options.recover === false) { + hydration_failed(); + } + init_operations(); + clear_text_content(target); + set_hydrating(false); + return mount(component, options); + } finally { + set_hydrating(was_hydrating); + set_hydrate_node(previous_hydrate_node); + } +} +const listeners = /* @__PURE__ */ new Map(); +function _mount(Component, { target, anchor, props = {}, events, context, intro = true, transformError }) { + init_operations(); + var component = void 0; + var unmount2 = component_root(() => { + var anchor_node = anchor ?? target.appendChild(create_text()); + boundary( + /** @type {TemplateNode} */ + anchor_node, + { + pending: () => { + } + }, + (anchor_node2) => { + push({}); + var ctx = ( + /** @type {ComponentContext} */ + component_context + ); + if (context) ctx.c = context; + if (events) { + props.$$events = events; + } + if (hydrating) { + assign_nodes( + /** @type {TemplateNode} */ + anchor_node2, + null + ); + } + component = Component(anchor_node2, props) || {}; + if (hydrating) { + active_effect.nodes.end = hydrate_node; + if (hydrate_node === null || hydrate_node.nodeType !== COMMENT_NODE || /** @type {Comment} */ + hydrate_node.data !== HYDRATION_END) { + hydration_mismatch(); + throw HYDRATION_ERROR; + } + } + pop(); + }, + transformError + ); + var registered_events = /* @__PURE__ */ new Set(); + var event_handle = (events2) => { + for (var i = 0; i < events2.length; i++) { + var event_name = events2[i]; + if (registered_events.has(event_name)) continue; + registered_events.add(event_name); + var passive = is_passive_event(event_name); + for (const node of [target, document]) { + var counts = listeners.get(node); + if (counts === void 0) { + counts = /* @__PURE__ */ new Map(); + listeners.set(node, counts); + } + var count = counts.get(event_name); + if (count === void 0) { + node.addEventListener(event_name, handle_event_propagation, { passive }); + counts.set(event_name, 1); + } else { + counts.set(event_name, count + 1); + } + } + } + }; + event_handle(array_from(all_registered_events)); + root_event_handles.add(event_handle); + return () => { + for (var event_name of registered_events) { + for (const node of [target, document]) { + var counts = ( + /** @type {Map} */ + listeners.get(node) + ); + var count = ( + /** @type {number} */ + counts.get(event_name) + ); + if (--count == 0) { + node.removeEventListener(event_name, handle_event_propagation); + counts.delete(event_name); + if (counts.size === 0) { + listeners.delete(node); + } + } else { + counts.set(event_name, count); + } + } + } + root_event_handles.delete(event_handle); + if (anchor_node !== anchor) { + anchor_node.parentNode?.removeChild(anchor_node); + } + }; + }); + mounted_components.set(component, unmount2); + return component; +} +let mounted_components = /* @__PURE__ */ new WeakMap(); +function unmount(component, options) { + const fn = mounted_components.get(component); + if (fn) { + mounted_components.delete(component); + return fn(options); + } + return Promise.resolve(); +} +function asClassComponent$1(component) { + return class extends Svelte4Component { + /** @param {any} options */ + constructor(options) { + super({ + component, + ...options + }); + } + }; +} +class Svelte4Component { + /** @type {any} */ + #events; + /** @type {Record} */ + #instance; + /** + * @param {ComponentConstructorOptions & { + * component: any; + * }} options + */ + constructor(options) { + var sources = /* @__PURE__ */ new Map(); + var add_source = (key, value) => { + var s = /* @__PURE__ */ mutable_source(value, false, false); + sources.set(key, s); + return s; + }; + const props = new Proxy( + { ...options.props || {}, $$events: {} }, + { + get(target, prop) { + return get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); + }, + has(target, prop) { + if (prop === LEGACY_PROPS) return true; + get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); + return Reflect.has(target, prop); + }, + set(target, prop, value) { + set(sources.get(prop) ?? add_source(prop, value), value); + return Reflect.set(target, prop, value); + } + } + ); + this.#instance = (options.hydrate ? hydrate : mount)(options.component, { + target: options.target, + anchor: options.anchor, + props, + context: options.context, + intro: options.intro ?? false, + recover: options.recover, + transformError: options.transformError + }); + if (!options?.props?.$$host || options.sync === false) { + flushSync(); + } + this.#events = props.$$events; + for (const key of Object.keys(this.#instance)) { + if (key === "$set" || key === "$destroy" || key === "$on") continue; + define_property(this, key, { + get() { + return this.#instance[key]; + }, + /** @param {any} value */ + set(value) { + this.#instance[key] = value; + }, + enumerable: true + }); + } + this.#instance.$set = /** @param {Record} next */ + (next2) => { + Object.assign(props, next2); + }; + this.#instance.$destroy = () => { + unmount(this.#instance); + }; + } + /** @param {Record} props */ + $set(props) { + this.#instance.$set(props); + } + /** + * @param {string} event + * @param {(...args: any[]) => any} callback + * @returns {any} + */ + $on(event, callback) { + this.#events[event] = this.#events[event] || []; + const cb = (...args) => callback.call(this, ...args); + this.#events[event].push(cb); + return () => { + this.#events[event] = this.#events[event].filter( + /** @param {any} fn */ + (fn) => fn !== cb + ); + }; + } + $destroy() { + this.#instance.$destroy(); + } +} +function asClassComponent(component) { + const component_constructor = asClassComponent$1(component); + const _render = (props, { context, csp, transformError } = {}) => { + const result = render(component, { props, context, csp, transformError }); + const munged = Object.defineProperties( + /** @type {LegacyRenderResult & PromiseLike} */ + {}, + { + css: { + value: { code: "", map: null } + }, + head: { + get: () => result.head + }, + html: { + get: () => result.body + }, + then: { + /** + * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function. + * + * @template TResult1 + * @template [TResult2=never] + * @param { (value: LegacyRenderResult) => TResult1 } onfulfilled + * @param { (reason: unknown) => TResult2 } onrejected + */ + value: (onfulfilled, onrejected) => { + { + const user_result = onfulfilled({ + css: munged.css, + head: munged.head, + html: munged.html + }); + return Promise.resolve(user_result); + } + } + } + } + ); + return munged; + }; + component_constructor.render = _render; + return component_constructor; +} +function Root($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { + stores, + page, + constructors, + components = [], + form, + data_0 = null, + data_1 = null, + data_2 = null + } = $$props; + { + setContext("__svelte__", stores); + } + { + stores.page.set(page); + } + const Pyramid_2 = derived(() => constructors[2]); + if (constructors[1]) { + $$renderer2.push(""); + const Pyramid_0 = constructors[0]; + if (Pyramid_0) { + $$renderer2.push(""); + Pyramid_0($$renderer2, { + data: data_0, + form, + params: page.params, + children: ($$renderer3) => { + if (constructors[2]) { + $$renderer3.push(""); + const Pyramid_1 = constructors[1]; + if (Pyramid_1) { + $$renderer3.push(""); + Pyramid_1($$renderer3, { + data: data_1, + form, + params: page.params, + children: ($$renderer4) => { + if (Pyramid_2()) { + $$renderer4.push(""); + Pyramid_2()($$renderer4, { data: data_2, form, params: page.params }); + $$renderer4.push(""); + } else { + $$renderer4.push(""); + $$renderer4.push(""); + } + }, + $$slots: { default: true } + }); + $$renderer3.push(""); + } else { + $$renderer3.push(""); + $$renderer3.push(""); + } + } else { + $$renderer3.push(""); + const Pyramid_1 = constructors[1]; + if (Pyramid_1) { + $$renderer3.push(""); + Pyramid_1($$renderer3, { data: data_1, form, params: page.params }); + $$renderer3.push(""); + } else { + $$renderer3.push(""); + $$renderer3.push(""); + } + } + $$renderer3.push(``); + }, + $$slots: { default: true } + }); + $$renderer2.push(""); + } else { + $$renderer2.push(""); + $$renderer2.push(""); + } + } else { + $$renderer2.push(""); + const Pyramid_0 = constructors[0]; + if (Pyramid_0) { + $$renderer2.push(""); + Pyramid_0($$renderer2, { data: data_0, form, params: page.params }); + $$renderer2.push(""); + } else { + $$renderer2.push(""); + $$renderer2.push(""); + } + } + $$renderer2.push(` `); + { + $$renderer2.push(""); + } + $$renderer2.push(``); + }); +} +const root = asClassComponent(Root); +export { + root as r, + safe_not_equal as s +}; diff --git a/.svelte-kit/adapter-node/chunks/routing.js b/.svelte-kit/adapter-node/chunks/routing.js new file mode 100644 index 0000000..11c0ffa --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/routing.js @@ -0,0 +1,85 @@ +import { b as decode_params } from "./url.js"; +function affects_path(segment) { + return segment !== "" && !/^\([^)]+\)$/.test(segment); +} +function get_route_segments(route) { + return route.slice(1).split("/").filter(affects_path); +} +function exec(match, params, matchers) { + const result = {}; + const values = match.slice(1); + const values_needing_match = values.filter((value) => value !== void 0); + let buffered = 0; + for (let i = 0; i < params.length; i += 1) { + const param = params[i]; + let value = values[i - buffered]; + if (param.chained && param.rest && buffered) { + value = values.slice(i - buffered, i + 1).filter((s) => s).join("/"); + buffered = 0; + } + if (value === void 0) { + if (param.rest) { + value = ""; + } else { + continue; + } + } + if (!param.matcher || matchers[param.matcher](value)) { + result[param.name] = value; + const next_param = params[i + 1]; + const next_value = values[i + 1]; + if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) { + buffered = 0; + } + if (!next_param && !next_value && Object.keys(result).length === values_needing_match.length) { + buffered = 0; + } + continue; + } + if (param.optional && param.chained) { + buffered++; + continue; + } + return; + } + if (buffered) return; + return result; +} +const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g; +function resolve_route(id, params) { + const segments = get_route_segments(id); + const has_id_trailing_slash = id.endsWith("/"); + return "/" + segments.map( + (segment) => segment.replace(basic_param_pattern, (_, optional, rest, name) => { + const param_value = params[name]; + if (!param_value) { + if (optional) return ""; + if (rest && param_value !== void 0) return ""; + throw new Error(`Missing parameter '${name}' in route ${id}`); + } + if (param_value.startsWith("/") || param_value.endsWith("/")) + throw new Error( + `Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar` + ); + return param_value; + }) + ).filter(Boolean).join("/") + (has_id_trailing_slash ? "/" : ""); +} +function find_route(path, routes, matchers) { + for (const route of routes) { + const match = route.pattern.exec(path); + if (!match) continue; + const matched = exec(match, route.params, matchers); + if (matched) { + return { + route, + params: decode_params(matched) + }; + } + } + return null; +} +export { + find_route as f, + resolve_route as r +}; diff --git a/.svelte-kit/adapter-node/chunks/server.js b/.svelte-kit/adapter-node/chunks/server.js new file mode 100644 index 0000000..bf7eaa6 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/server.js @@ -0,0 +1,27 @@ +let base = ""; +let assets = base; +const app_dir = "_app"; +const relative = true; +const initial = { base, assets }; +const initial_base = initial.base; +function override(paths) { + base = paths.base; + assets = paths.assets; +} +function reset() { + base = initial.base; + assets = initial.assets; +} +function set_assets(path) { + assets = initial.assets = path; +} +export { + assets as a, + base as b, + app_dir as c, + reset as d, + initial_base as i, + override as o, + relative as r, + set_assets as s +}; diff --git a/.svelte-kit/adapter-node/chunks/shared-server.js b/.svelte-kit/adapter-node/chunks/shared-server.js new file mode 100644 index 0000000..4fa13e6 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/shared-server.js @@ -0,0 +1,14 @@ +let private_env = {}; +let public_env = {}; +function set_private_env(environment) { + private_env = environment; +} +function set_public_env(environment) { + public_env = environment; +} +export { + set_public_env as a, + private_env as b, + public_env as p, + set_private_env as s +}; diff --git a/.svelte-kit/adapter-node/chunks/shared.js b/.svelte-kit/adapter-node/chunks/shared.js new file mode 100644 index 0000000..9162124 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/shared.js @@ -0,0 +1,789 @@ +import { json, text } from "@sveltejs/kit"; +import { SvelteKitError, HttpError } from "@sveltejs/kit/internal"; +import { with_request_store } from "@sveltejs/kit/internal/server"; +import * as devalue from "devalue"; +import { t as text_decoder, b as base64_encode, c as base64_decode } from "./utils.js"; +const SVELTE_KIT_ASSETS = "/_svelte_kit_assets"; +const ENDPOINT_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]; +const MUTATIVE_METHODS = ["POST", "PUT", "PATCH", "DELETE"]; +const PAGE_METHODS = ["GET", "POST", "HEAD"]; +function set_nested_value(object, path_string, value) { + if (path_string.startsWith("n:")) { + path_string = path_string.slice(2); + value = value === "" ? void 0 : parseFloat(value); + } else if (path_string.startsWith("b:")) { + path_string = path_string.slice(2); + value = value === "on"; + } + deep_set(object, split_path(path_string), value); +} +function convert_formdata(data) { + const result = {}; + for (let key of data.keys()) { + const is_array = key.endsWith("[]"); + let values = data.getAll(key); + if (is_array) key = key.slice(0, -2); + if (values.length > 1 && !is_array) { + throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`); + } + values = values.filter( + (entry) => typeof entry === "string" || entry.name !== "" || entry.size > 0 + ); + if (key.startsWith("n:")) { + key = key.slice(2); + values = values.map((v) => v === "" ? void 0 : parseFloat( + /** @type {string} */ + v + )); + } else if (key.startsWith("b:")) { + key = key.slice(2); + values = values.map((v) => v === "on"); + } + set_nested_value(result, key, is_array ? values : values[0]); + } + return result; +} +const BINARY_FORM_CONTENT_TYPE = "application/x-sveltekit-formdata"; +const BINARY_FORM_VERSION = 0; +const HEADER_BYTES = 1 + 4 + 2; +async function deserialize_binary_form(request) { + if (request.headers.get("content-type") !== BINARY_FORM_CONTENT_TYPE) { + const form_data = await request.formData(); + return { data: convert_formdata(form_data), meta: {}, form_data }; + } + if (!request.body) { + throw deserialize_error("no body"); + } + const content_length = parseInt(request.headers.get("content-length") ?? ""); + if (Number.isNaN(content_length)) { + throw deserialize_error("invalid Content-Length header"); + } + const reader = request.body.getReader(); + const chunks = []; + function get_chunk(index) { + if (index in chunks) return chunks[index]; + let i = chunks.length; + while (i <= index) { + chunks[i] = reader.read().then((chunk) => chunk.value); + i++; + } + return chunks[index]; + } + async function get_buffer(offset, length) { + let start_chunk; + let chunk_start = 0; + let chunk_index; + for (chunk_index = 0; ; chunk_index++) { + const chunk = await get_chunk(chunk_index); + if (!chunk) return null; + const chunk_end = chunk_start + chunk.byteLength; + if (offset >= chunk_start && offset < chunk_end) { + start_chunk = chunk; + break; + } + chunk_start = chunk_end; + } + if (offset + length <= chunk_start + start_chunk.byteLength) { + return start_chunk.subarray(offset - chunk_start, offset + length - chunk_start); + } + const chunks2 = [start_chunk.subarray(offset - chunk_start)]; + let cursor = start_chunk.byteLength - offset + chunk_start; + while (cursor < length) { + chunk_index++; + let chunk = await get_chunk(chunk_index); + if (!chunk) return null; + if (chunk.byteLength > length - cursor) { + chunk = chunk.subarray(0, length - cursor); + } + chunks2.push(chunk); + cursor += chunk.byteLength; + } + const buffer = new Uint8Array(length); + cursor = 0; + for (const chunk of chunks2) { + buffer.set(chunk, cursor); + cursor += chunk.byteLength; + } + return buffer; + } + const header = await get_buffer(0, HEADER_BYTES); + if (!header) throw deserialize_error("too short"); + if (header[0] !== BINARY_FORM_VERSION) { + throw deserialize_error(`got version ${header[0]}, expected version ${BINARY_FORM_VERSION}`); + } + const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength); + const data_length = header_view.getUint32(1, true); + if (HEADER_BYTES + data_length > content_length) { + throw deserialize_error("data overflow"); + } + const file_offsets_length = header_view.getUint16(5, true); + if (HEADER_BYTES + data_length + file_offsets_length > content_length) { + throw deserialize_error("file offset table overflow"); + } + const data_buffer = await get_buffer(HEADER_BYTES, data_length); + if (!data_buffer) throw deserialize_error("data too short"); + let file_offsets; + let files_start_offset; + if (file_offsets_length > 0) { + const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length); + if (!file_offsets_buffer) throw deserialize_error("file offset table too short"); + const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer)); + if (!Array.isArray(parsed_offsets) || parsed_offsets.some((n) => typeof n !== "number" || !Number.isInteger(n) || n < 0)) { + throw deserialize_error("invalid file offset table"); + } + file_offsets = /** @type {Array} */ + parsed_offsets; + files_start_offset = HEADER_BYTES + data_length + file_offsets_length; + } + const file_spans = []; + const [data, meta] = devalue.parse(text_decoder.decode(data_buffer), { + File: ([name, type, size, last_modified, index]) => { + if (typeof name !== "string" || typeof type !== "string" || typeof size !== "number" || typeof last_modified !== "number" || typeof index !== "number") { + throw deserialize_error("invalid file metadata"); + } + let offset = file_offsets[index]; + if (offset === void 0) { + throw deserialize_error("duplicate file offset table index"); + } + file_offsets[index] = void 0; + offset += files_start_offset; + if (offset + size > content_length) { + throw deserialize_error("file data overflow"); + } + file_spans.push({ offset, size }); + return new Proxy(new LazyFile(name, type, size, last_modified, get_chunk, offset), { + getPrototypeOf() { + return File.prototype; + } + }); + } + }); + file_spans.sort((a, b) => a.offset - b.offset || a.size - b.size); + for (let i = 1; i < file_spans.length; i++) { + const previous = file_spans[i - 1]; + const current = file_spans[i]; + const previous_end = previous.offset + previous.size; + if (previous_end < current.offset) { + throw deserialize_error("gaps in file data"); + } + if (previous_end > current.offset) { + throw deserialize_error("overlapping file data"); + } + } + void (async () => { + let has_more = true; + while (has_more) { + const chunk = await get_chunk(chunks.length); + has_more = !!chunk; + } + })(); + return { data, meta, form_data: null }; +} +function deserialize_error(message) { + return new SvelteKitError(400, "Bad Request", `Could not deserialize binary form: ${message}`); +} +class LazyFile { + /** @type {(index: number) => Promise | undefined>} */ + #get_chunk; + /** @type {number} */ + #offset; + /** + * @param {string} name + * @param {string} type + * @param {number} size + * @param {number} last_modified + * @param {(index: number) => Promise | undefined>} get_chunk + * @param {number} offset + */ + constructor(name, type, size, last_modified, get_chunk, offset) { + this.name = name; + this.type = type; + this.size = size; + this.lastModified = last_modified; + this.webkitRelativePath = ""; + this.#get_chunk = get_chunk; + this.#offset = offset; + this.arrayBuffer = this.arrayBuffer.bind(this); + this.bytes = this.bytes.bind(this); + this.slice = this.slice.bind(this); + this.stream = this.stream.bind(this); + this.text = this.text.bind(this); + } + /** @type {ArrayBuffer | undefined} */ + #buffer; + async arrayBuffer() { + this.#buffer ??= await new Response(this.stream()).arrayBuffer(); + return this.#buffer; + } + async bytes() { + return new Uint8Array(await this.arrayBuffer()); + } + /** + * @param {number=} start + * @param {number=} end + * @param {string=} contentType + */ + slice(start = 0, end = this.size, contentType = this.type) { + if (start < 0) { + start = Math.max(this.size + start, 0); + } else { + start = Math.min(start, this.size); + } + if (end < 0) { + end = Math.max(this.size + end, 0); + } else { + end = Math.min(end, this.size); + } + const size = Math.max(end - start, 0); + const file = new LazyFile( + this.name, + contentType, + size, + this.lastModified, + this.#get_chunk, + this.#offset + start + ); + return file; + } + stream() { + let cursor = 0; + let chunk_index = 0; + return new ReadableStream({ + start: async (controller) => { + let chunk_start = 0; + let start_chunk; + for (chunk_index = 0; ; chunk_index++) { + const chunk = await this.#get_chunk(chunk_index); + if (!chunk) return null; + const chunk_end = chunk_start + chunk.byteLength; + if (this.#offset >= chunk_start && this.#offset < chunk_end) { + start_chunk = chunk; + break; + } + chunk_start = chunk_end; + } + if (this.#offset + this.size <= chunk_start + start_chunk.byteLength) { + controller.enqueue( + start_chunk.subarray(this.#offset - chunk_start, this.#offset + this.size - chunk_start) + ); + controller.close(); + } else { + controller.enqueue(start_chunk.subarray(this.#offset - chunk_start)); + cursor = start_chunk.byteLength - this.#offset + chunk_start; + } + }, + pull: async (controller) => { + chunk_index++; + let chunk = await this.#get_chunk(chunk_index); + if (!chunk) { + controller.error("incomplete file data"); + controller.close(); + return; + } + if (chunk.byteLength > this.size - cursor) { + chunk = chunk.subarray(0, this.size - cursor); + } + controller.enqueue(chunk); + cursor += chunk.byteLength; + if (cursor >= this.size) { + controller.close(); + } + } + }); + } + async text() { + return text_decoder.decode(await this.arrayBuffer()); + } +} +const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/; +function split_path(path) { + if (!path_regex.test(path)) { + throw new Error(`Invalid path ${path}`); + } + return path.split(/\.|\[|\]/).filter(Boolean); +} +function check_prototype_pollution(key) { + if (key === "__proto__" || key === "constructor" || key === "prototype") { + throw new Error( + `Invalid key "${key}"` + ); + } +} +function deep_set(object, keys, value) { + let current = object; + for (let i = 0; i < keys.length - 1; i += 1) { + const key = keys[i]; + check_prototype_pollution(key); + const is_array = /^\d+$/.test(keys[i + 1]); + const exists = Object.hasOwn(current, key); + const inner = current[key]; + if (exists && is_array !== Array.isArray(inner)) { + throw new Error(`Invalid array key ${keys[i + 1]}`); + } + if (!exists) { + current[key] = is_array ? [] : {}; + } + current = current[key]; + } + const final_key = keys[keys.length - 1]; + check_prototype_pollution(final_key); + current[final_key] = value; +} +function normalize_issue(issue, server = false) { + const normalized = { name: "", path: [], message: issue.message, server }; + if (issue.path !== void 0) { + let name = ""; + for (const segment of issue.path) { + const key = ( + /** @type {string | number} */ + typeof segment === "object" ? segment.key : segment + ); + normalized.path.push(key); + if (typeof key === "number") { + name += `[${key}]`; + } else if (typeof key === "string") { + name += name === "" ? key : "." + key; + } + } + normalized.name = name; + } + return normalized; +} +function flatten_issues(issues) { + const result = {}; + for (const issue of issues) { + (result.$ ??= []).push(issue); + let name = ""; + if (issue.path !== void 0) { + for (const key of issue.path) { + if (typeof key === "number") { + name += `[${key}]`; + } else if (typeof key === "string") { + name += name === "" ? key : "." + key; + } + (result[name] ??= []).push(issue); + } + } + } + return result; +} +function deep_get(object, path) { + let current = object; + for (const key of path) { + if (current == null || typeof current !== "object") { + return current; + } + current = current[key]; + } + return current; +} +function create_field_proxy(target, get_input, set_input, get_issues, path = []) { + const get_value = () => { + return deep_get(get_input(), path); + }; + return new Proxy(target, { + get(target2, prop) { + if (typeof prop === "symbol") return target2[prop]; + if (/^\d+$/.test(prop)) { + return create_field_proxy({}, get_input, set_input, get_issues, [ + ...path, + parseInt(prop, 10) + ]); + } + const key = build_path_string(path); + if (prop === "set") { + const set_func = function(newValue) { + set_input(path, newValue); + return newValue; + }; + return create_field_proxy(set_func, get_input, set_input, get_issues, [...path, prop]); + } + if (prop === "value") { + return create_field_proxy(get_value, get_input, set_input, get_issues, [...path, prop]); + } + if (prop === "issues" || prop === "allIssues") { + const issues_func = () => { + const all_issues = get_issues()[key === "" ? "$" : key]; + if (prop === "allIssues") { + return all_issues?.map((issue) => ({ + path: issue.path, + message: issue.message + })); + } + return all_issues?.filter((issue) => issue.name === key)?.map((issue) => ({ + path: issue.path, + message: issue.message + })); + }; + return create_field_proxy(issues_func, get_input, set_input, get_issues, [...path, prop]); + } + if (prop === "as") { + const as_func = (type, input_value) => { + const is_array = type === "file multiple" || type === "select multiple" || type === "checkbox" && typeof input_value === "string"; + const prefix = type === "number" || type === "range" ? "n:" : type === "checkbox" && !is_array ? "b:" : ""; + const base_props = { + name: prefix + key + (is_array ? "[]" : ""), + get "aria-invalid"() { + const issues = get_issues(); + return key in issues ? "true" : void 0; + } + }; + if (type !== "text" && type !== "select" && type !== "select multiple") { + base_props.type = type === "file multiple" ? "file" : type; + } + if (type === "submit" || type === "hidden") { + return Object.defineProperties(base_props, { + value: { value: input_value, enumerable: true } + }); + } + if (type === "select" || type === "select multiple") { + return Object.defineProperties(base_props, { + multiple: { value: is_array, enumerable: true }, + value: { + enumerable: true, + get() { + return get_value(); + } + } + }); + } + if (type === "checkbox" || type === "radio") { + return Object.defineProperties(base_props, { + value: { value: input_value ?? "on", enumerable: true }, + checked: { + enumerable: true, + get() { + const value = get_value(); + if (type === "radio") { + return value === input_value; + } + if (is_array) { + return (value ?? []).includes(input_value); + } + return value; + } + } + }); + } + if (type === "file" || type === "file multiple") { + return Object.defineProperties(base_props, { + multiple: { value: is_array, enumerable: true }, + files: { + enumerable: true, + get() { + const value = get_value(); + if (value instanceof File) { + if (typeof DataTransfer !== "undefined") { + const fileList = new DataTransfer(); + fileList.items.add(value); + return fileList.files; + } + return { 0: value, length: 1 }; + } + if (Array.isArray(value) && value.every((f) => f instanceof File)) { + if (typeof DataTransfer !== "undefined") { + const fileList = new DataTransfer(); + value.forEach((file) => fileList.items.add(file)); + return fileList.files; + } + const fileListLike = { length: value.length }; + value.forEach((file, index) => { + fileListLike[index] = file; + }); + return fileListLike; + } + return null; + } + } + }); + } + return Object.defineProperties(base_props, { + value: { + enumerable: true, + get() { + const value = get_value(); + return value != null ? String(value) : ""; + } + } + }); + }; + return create_field_proxy(as_func, get_input, set_input, get_issues, [...path, "as"]); + } + return create_field_proxy({}, get_input, set_input, get_issues, [...path, prop]); + } + }); +} +function build_path_string(path) { + let result = ""; + for (const segment of path) { + if (typeof segment === "number") { + result += `[${segment}]`; + } else { + result += result === "" ? segment : "." + segment; + } + } + return result; +} +function negotiate(accept, types) { + const parts = []; + accept.split(",").forEach((str, i) => { + const match = /([^/ \t]+)\/([^; \t]+)[ \t]*(?:;[ \t]*q=([0-9.]+))?/.exec(str); + if (match) { + const [, type, subtype, q = "1"] = match; + parts.push({ type, subtype, q: +q, i }); + } + }); + parts.sort((a, b) => { + if (a.q !== b.q) { + return b.q - a.q; + } + if (a.subtype === "*" !== (b.subtype === "*")) { + return a.subtype === "*" ? 1 : -1; + } + if (a.type === "*" !== (b.type === "*")) { + return a.type === "*" ? 1 : -1; + } + return a.i - b.i; + }); + let accepted; + let min_priority = Infinity; + for (const mimetype of types) { + const [type, subtype] = mimetype.split("/"); + const priority = parts.findIndex( + (part) => (part.type === type || part.type === "*") && (part.subtype === subtype || part.subtype === "*") + ); + if (priority !== -1 && priority < min_priority) { + accepted = mimetype; + min_priority = priority; + } + } + return accepted; +} +function is_content_type(request, ...types) { + const type = request.headers.get("content-type")?.split(";", 1)[0].trim() ?? ""; + return types.includes(type.toLowerCase()); +} +function is_form_content_type(request) { + return is_content_type( + request, + "application/x-www-form-urlencoded", + "multipart/form-data", + "text/plain", + BINARY_FORM_CONTENT_TYPE + ); +} +function coalesce_to_error(err) { + return err instanceof Error || err && /** @type {any} */ + err.name && /** @type {any} */ + err.message ? ( + /** @type {Error} */ + err + ) : new Error(JSON.stringify(err)); +} +function normalize_error(error) { + return ( + /** @type {import('../exports/internal/index.js').Redirect | HttpError | SvelteKitError | Error} */ + error + ); +} +function get_status(error) { + return error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500; +} +function get_message(error) { + return error instanceof SvelteKitError ? error.text : "Internal Error"; +} +const escape_html_attr_dict = { + "&": "&", + '"': """ + // Svelte also escapes < because the escape function could be called inside a `noscript` there + // https://github.com/sveltejs/svelte/security/advisories/GHSA-8266-84wp-wv5c + // However, that doesn't apply in SvelteKit +}; +const escape_html_dict = { + "&": "&", + "<": "<" +}; +const surrogates = ( + // high surrogate without paired low surrogate + "[\\ud800-\\udbff](?![\\udc00-\\udfff])|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\udc00-\\udfff]" +); +const escape_html_attr_regex = new RegExp( + `[${Object.keys(escape_html_attr_dict).join("")}]|` + surrogates, + "g" +); +const escape_html_regex = new RegExp( + `[${Object.keys(escape_html_dict).join("")}]|` + surrogates, + "g" +); +function escape_html(str, is_attr) { + const dict = is_attr ? escape_html_attr_dict : escape_html_dict; + const escaped_str = str.replace(is_attr ? escape_html_attr_regex : escape_html_regex, (match) => { + if (match.length === 2) { + return match; + } + return dict[match] ?? `&#${match.charCodeAt(0)};`; + }); + return escaped_str; +} +function method_not_allowed(mod, method) { + return text(`${method} method not allowed`, { + status: 405, + headers: { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: allowed_methods(mod).join(", ") + } + }); +} +function allowed_methods(mod) { + const allowed = ENDPOINT_METHODS.filter((method) => method in mod); + if ("GET" in mod && !("HEAD" in mod)) { + allowed.push("HEAD"); + } + return allowed; +} +function get_global_name(options) { + return `__sveltekit_${options.version_hash}`; +} +function static_error_page(options, status, message) { + let page = options.templates.error({ status, message: escape_html(message) }); + return text(page, { + headers: { "content-type": "text/html; charset=utf-8" }, + status + }); +} +async function handle_fatal_error(event, state, options, error) { + error = error instanceof HttpError ? error : coalesce_to_error(error); + const status = get_status(error); + const body = await handle_error_and_jsonify(event, state, options, error); + const type = negotiate(event.request.headers.get("accept") || "text/html", [ + "application/json", + "text/html" + ]); + if (event.isDataRequest || type === "application/json") { + return json(body, { + status + }); + } + return static_error_page(options, status, body.message); +} +async function handle_error_and_jsonify(event, state, options, error) { + if (error instanceof HttpError) { + return { message: "Unknown Error", ...error.body }; + } + const status = get_status(error); + const message = get_message(error); + return await with_request_store( + { event, state }, + () => options.hooks.handleError({ error, event, status, message }) + ) ?? { message }; +} +function redirect_response(status, location) { + const response = new Response(void 0, { + status, + headers: { location } + }); + return response; +} +function clarify_devalue_error(event, error) { + if (error.path) { + return `Data returned from \`load\` while rendering ${event.route.id} is not serializable: ${error.message} (${error.path}). If you need to serialize/deserialize custom types, use transport hooks: https://svelte.dev/docs/kit/hooks#Universal-hooks-transport.`; + } + if (error.path === "") { + return `Data returned from \`load\` while rendering ${event.route.id} is not a plain object`; + } + return error.message; +} +function serialize_uses(node) { + const uses = {}; + if (node.uses && node.uses.dependencies.size > 0) { + uses.dependencies = Array.from(node.uses.dependencies); + } + if (node.uses && node.uses.search_params.size > 0) { + uses.search_params = Array.from(node.uses.search_params); + } + if (node.uses && node.uses.params.size > 0) { + uses.params = Array.from(node.uses.params); + } + if (node.uses?.parent) uses.parent = 1; + if (node.uses?.route) uses.route = 1; + if (node.uses?.url) uses.url = 1; + return uses; +} +function has_prerendered_path(manifest, pathname) { + return manifest._.prerendered_routes.has(pathname) || pathname.at(-1) === "/" && manifest._.prerendered_routes.has(pathname.slice(0, -1)); +} +function format_server_error(status, error, event) { + const formatted_text = ` +\x1B[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1B[0m`; + if (status === 404) { + return formatted_text; + } + return `${formatted_text} +${error.stack}`; +} +function get_node_type(node_id) { + const parts = node_id?.split("/"); + const filename = parts?.at(-1); + if (!filename) return "unknown"; + const dot_parts = filename.split("."); + return dot_parts.slice(0, -1).join("."); +} +const INVALIDATED_PARAM = "x-sveltekit-invalidated"; +const TRAILING_SLASH_PARAM = "x-sveltekit-trailing-slash"; +function stringify(data, transport) { + const encoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.encode])); + return devalue.stringify(data, encoders); +} +function stringify_remote_arg(value, transport) { + if (value === void 0) return ""; + const json_string = stringify(value, transport); + const bytes = new TextEncoder().encode(json_string); + return base64_encode(bytes).replaceAll("=", "").replaceAll("+", "-").replaceAll("/", "_"); +} +function parse_remote_arg(string, transport) { + if (!string) return void 0; + const json_string = text_decoder.decode( + // no need to add back `=` characters, atob can handle it + base64_decode(string.replaceAll("-", "+").replaceAll("_", "/")) + ); + const decoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.decode])); + return devalue.parse(json_string, decoders); +} +function create_remote_key(id, payload) { + return id + "/" + payload; +} +export { + ENDPOINT_METHODS as E, + INVALIDATED_PARAM as I, + MUTATIVE_METHODS as M, + PAGE_METHODS as P, + SVELTE_KIT_ASSETS as S, + TRAILING_SLASH_PARAM as T, + normalize_error as a, + get_global_name as b, + clarify_devalue_error as c, + get_node_type as d, + escape_html as e, + create_remote_key as f, + get_status as g, + handle_error_and_jsonify as h, + is_form_content_type as i, + static_error_page as j, + stringify as k, + deserialize_binary_form as l, + method_not_allowed as m, + negotiate as n, + has_prerendered_path as o, + parse_remote_arg as p, + handle_fatal_error as q, + redirect_response as r, + serialize_uses as s, + format_server_error as t, + create_field_proxy as u, + normalize_issue as v, + set_nested_value as w, + flatten_issues as x, + deep_set as y, + stringify_remote_arg as z +}; diff --git a/.svelte-kit/adapter-node/chunks/state.svelte.js b/.svelte-kit/adapter-node/chunks/state.svelte.js new file mode 100644 index 0000000..9cd4558 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/state.svelte.js @@ -0,0 +1,18 @@ +import "clsx"; +import { n as noop } from "./index3.js"; +import "./url.js"; +import "@sveltejs/kit/internal/server"; +import "./root.js"; +const is_legacy = noop.toString().includes("$$") || /function \w+\(\) \{\}/.test(noop.toString()); +if (is_legacy) { + ({ + data: {}, + form: null, + error: null, + params: {}, + route: { id: null }, + state: {}, + status: -1, + url: new URL("https://example.com") + }); +} diff --git a/.svelte-kit/adapter-node/chunks/url.js b/.svelte-kit/adapter-node/chunks/url.js new file mode 100644 index 0000000..2ba2c18 --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/url.js @@ -0,0 +1,106 @@ +const SCHEME = /^[a-z][a-z\d+\-.]+:/i; +const internal = new URL("sveltekit-internal://"); +function resolve(base, path) { + if (path[0] === "/" && path[1] === "/") return path; + let url = new URL(base, internal); + url = new URL(path, url); + return url.protocol === internal.protocol ? url.pathname + url.search + url.hash : url.href; +} +function normalize_path(path, trailing_slash) { + if (path === "/" || trailing_slash === "ignore") return path; + if (trailing_slash === "never") { + return path.endsWith("/") ? path.slice(0, -1) : path; + } else if (trailing_slash === "always" && !path.endsWith("/")) { + return path + "/"; + } + return path; +} +function decode_pathname(pathname) { + return pathname.split("%25").map(decodeURI).join("%25"); +} +function decode_params(params) { + for (const key in params) { + params[key] = decodeURIComponent(params[key]); + } + return params; +} +function make_trackable(url, callback, search_params_callback, allow_hash = false) { + const tracked = new URL(url); + Object.defineProperty(tracked, "searchParams", { + value: new Proxy(tracked.searchParams, { + get(obj, key) { + if (key === "get" || key === "getAll" || key === "has") { + return (param, ...rest) => { + search_params_callback(param); + return obj[key](param, ...rest); + }; + } + callback(); + const value = Reflect.get(obj, key); + return typeof value === "function" ? value.bind(obj) : value; + } + }), + enumerable: true, + configurable: true + }); + const tracked_url_properties = ["href", "pathname", "search", "toString", "toJSON"]; + if (allow_hash) tracked_url_properties.push("hash"); + for (const property of tracked_url_properties) { + Object.defineProperty(tracked, property, { + get() { + callback(); + return url[property]; + }, + enumerable: true, + configurable: true + }); + } + { + tracked[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { + return inspect(url, opts); + }; + tracked.searchParams[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { + return inspect(url.searchParams, opts); + }; + } + if (!allow_hash) { + disable_hash(tracked); + } + return tracked; +} +function disable_hash(url) { + allow_nodejs_console_log(url); + Object.defineProperty(url, "hash", { + get() { + throw new Error( + "Cannot access event.url.hash. Consider using `page.url.hash` inside a component instead" + ); + } + }); +} +function disable_search(url) { + allow_nodejs_console_log(url); + for (const property of ["search", "searchParams"]) { + Object.defineProperty(url, property, { + get() { + throw new Error(`Cannot access url.${property} on a page with prerendering enabled`); + } + }); + } +} +function allow_nodejs_console_log(url) { + { + url[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { + return inspect(new URL(url), opts); + }; + } +} +export { + SCHEME as S, + decode_pathname as a, + decode_params as b, + disable_search as d, + make_trackable as m, + normalize_path as n, + resolve as r +}; diff --git a/.svelte-kit/adapter-node/chunks/utils.js b/.svelte-kit/adapter-node/chunks/utils.js new file mode 100644 index 0000000..78e5bde --- /dev/null +++ b/.svelte-kit/adapter-node/chunks/utils.js @@ -0,0 +1,43 @@ +const text_encoder = new TextEncoder(); +const text_decoder = new TextDecoder(); +function get_relative_path(from, to) { + const from_parts = from.split(/[/\\]/); + const to_parts = to.split(/[/\\]/); + from_parts.pop(); + while (from_parts[0] === to_parts[0]) { + from_parts.shift(); + to_parts.shift(); + } + let i = from_parts.length; + while (i--) from_parts[i] = ".."; + return from_parts.concat(to_parts).join("/"); +} +function base64_encode(bytes) { + if (globalThis.Buffer) { + return globalThis.Buffer.from(bytes).toString("base64"); + } + let binary = ""; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} +function base64_decode(encoded) { + if (globalThis.Buffer) { + const buffer = globalThis.Buffer.from(encoded, "base64"); + return new Uint8Array(buffer); + } + const binary = atob(encoded); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +} +export { + text_encoder as a, + base64_encode as b, + base64_decode as c, + get_relative_path as g, + text_decoder as t +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/health/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/health/_server.ts.js new file mode 100644 index 0000000..203529d --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/health/_server.ts.js @@ -0,0 +1,25 @@ +import { json } from "@sveltejs/kit"; +import { d as db } from "../../../../chunks/index2.js"; +import { sql } from "drizzle-orm"; +const GET = async () => { + try { + await db.execute(sql`SELECT 1`); + return json({ + status: "ok", + timestamp: (/* @__PURE__ */ new Date()).toISOString(), + uptime: process.uptime() + }); + } catch (e) { + return json( + { + status: "error", + error: "Database connection failed", + timestamp: (/* @__PURE__ */ new Date()).toISOString() + }, + { status: 503 } + ); + } +}; +export { + GET +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/_server.ts.js new file mode 100644 index 0000000..4d6b270 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/_server.ts.js @@ -0,0 +1,24 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, m as mappool, a as mappoolSlot } from "../../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const GET = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const pool = await db.query.mappool.findFirst({ + where: eq(mappool.id, params.id) + }); + if (!pool) error(404, "Mappool not found"); + const slots = await db.query.mappoolSlot.findMany({ + where: eq(mappoolSlot.mappoolId, params.id), + orderBy: (s, { asc }) => [asc(s.category), asc(s.orderInCategory)] + }); + return json({ ...pool, slots }); +}; +const DELETE = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + await db.delete(mappool).where(eq(mappool.id, params.id)); + return json({ ok: true }); +}; +export { + DELETE, + GET +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/slots/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/slots/_server.ts.js new file mode 100644 index 0000000..b61da5c --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/slots/_server.ts.js @@ -0,0 +1,27 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, a as mappoolSlot } from "../../../../../../chunks/index2.js"; +import { and, eq } from "drizzle-orm"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { beatmapId, category, mods } = await request.json(); + if (!beatmapId || !category) { + error(400, "beatmapId and category are required"); + } + const existing = await db.query.mappoolSlot.findMany({ + where: and( + eq(mappoolSlot.mappoolId, params.id), + eq(mappoolSlot.category, category.toUpperCase()) + ) + }); + const [created] = await db.insert(mappoolSlot).values({ + mappoolId: params.id, + beatmapId: String(beatmapId), + category: category.toUpperCase(), + orderInCategory: existing.length + 1, + mods: mods ?? [] + }).returning(); + return json(created, { status: 201 }); +}; +export { + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js new file mode 100644 index 0000000..c77f59a --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js @@ -0,0 +1,11 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, a as mappoolSlot } from "../../../../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const DELETE = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + await db.delete(mappoolSlot).where(eq(mappoolSlot.id, params.slotId)); + return json({ ok: true }); +}; +export { + DELETE +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_server.ts.js new file mode 100644 index 0000000..bf904be --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/mappools/_server.ts.js @@ -0,0 +1,23 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, m as mappool } from "../../../../chunks/index2.js"; +const GET = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const mappools = await db.query.mappool.findMany({ + orderBy: (m, { desc }) => [desc(m.createdAt)] + }); + return json(mappools); +}; +const POST = async ({ request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { name } = await request.json(); + if (!name?.trim()) error(400, "Name is required"); + const [created] = await db.insert(mappool).values({ + name: name.trim(), + createdBy: locals.user.id + }).returning(); + return json(created, { status: 201 }); +}; +export { + GET, + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/_server.ts.js new file mode 100644 index 0000000..b383e98 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/_server.ts.js @@ -0,0 +1,24 @@ +import { error, json } from "@sveltejs/kit"; +import { a as cancelMatch, g as getMatchFull } from "../../../../../chunks/engine.js"; +const GET = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const m = await getMatchFull(params.id); + return json(m); + } catch (e) { + error(404, e.message); + } +}; +const DELETE = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const m = await cancelMatch(params.id); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + DELETE, + GET +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/lobby/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/lobby/_server.ts.js new file mode 100644 index 0000000..35ed7aa --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/lobby/_server.ts.js @@ -0,0 +1,15 @@ +import { error, json } from "@sveltejs/kit"; +import { m as moveToLobby } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const body = await request.json().catch(() => ({})); + try { + const m = await moveToLobby(params.id, body.osuLobbyId); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/pick/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/pick/_server.ts.js new file mode 100644 index 0000000..a655c49 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/pick/_server.ts.js @@ -0,0 +1,18 @@ +import { error, json } from "@sveltejs/kit"; +import { p as pickMap } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { participantId, mappoolSlotId } = await request.json(); + if (!participantId || !mappoolSlotId) { + error(400, "participantId and mappoolSlotId required"); + } + try { + const game = await pickMap(params.id, participantId, mappoolSlotId); + return json(game); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/roll/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/roll/_server.ts.js new file mode 100644 index 0000000..21f102e --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/roll/_server.ts.js @@ -0,0 +1,18 @@ +import { error, json } from "@sveltejs/kit"; +import { s as submitRoll } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { participantId, value } = await request.json(); + if (!participantId || value == null) { + error(400, "participantId and value required"); + } + try { + const m = await submitRoll(params.id, participantId, value); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/rolling/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/rolling/_server.ts.js new file mode 100644 index 0000000..e5cc1e3 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/rolling/_server.ts.js @@ -0,0 +1,14 @@ +import { error, json } from "@sveltejs/kit"; +import { b as moveToRolling } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const m = await moveToRolling(params.id); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/score/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/score/_server.ts.js new file mode 100644 index 0000000..cd031cb --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_id_/score/_server.ts.js @@ -0,0 +1,18 @@ +import { error, json } from "@sveltejs/kit"; +import { d as submitGameScores } from "../../../../../../chunks/engine.js"; +const POST = async ({ request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { matchGameId, scores } = await request.json(); + if (!matchGameId || !scores?.length) { + error(400, "matchGameId and scores required"); + } + try { + const m = await submitGameScores(matchGameId, scores); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/matches/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_server.ts.js new file mode 100644 index 0000000..3732a25 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/matches/_server.ts.js @@ -0,0 +1,37 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db } from "../../../../chunks/index2.js"; +import { c as createMatch } from "../../../../chunks/engine.js"; +const GET = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const matches = await db.query.match.findMany({ + with: { + participants: { with: { team: true } } + }, + orderBy: (m, { desc }) => [desc(m.createdAt)] + }); + return json(matches); +}; +const POST = async ({ request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const body = await request.json(); + const { name, config, mappoolId, teams } = body; + if (!name || !config || !mappoolId || !teams?.length) { + error(400, "Missing required fields"); + } + try { + const result = await createMatch({ + name, + config, + mappoolId, + teams, + createdBy: locals.user.id + }); + return json(result, { status: 201 }); + } catch (e) { + error(400, e.message); + } +}; +export { + GET, + POST +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/osu/beatmap/_id_/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/osu/beatmap/_id_/_server.ts.js new file mode 100644 index 0000000..c258470 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/osu/beatmap/_id_/_server.ts.js @@ -0,0 +1,14 @@ +import { error, json } from "@sveltejs/kit"; +import { g as getBeatmap } from "../../../../../../chunks/api.js"; +const GET = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const beatmap = await getBeatmap(params.id); + return json(beatmap); + } catch { + error(404, "Beatmap not found"); + } +}; +export { + GET +}; diff --git a/.svelte-kit/adapter-node/entries/endpoints/api/queue/_server.ts.js b/.svelte-kit/adapter-node/entries/endpoints/api/queue/_server.ts.js new file mode 100644 index 0000000..d6b0508 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/endpoints/api/queue/_server.ts.js @@ -0,0 +1,36 @@ +import { error, json } from "@sveltejs/kit"; +import { l as leaveQueue, e as getQueueStatus, j as joinQueue } from "../../../../chunks/engine.js"; +import { d as db, t as teamMember } from "../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const GET = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const status = await getQueueStatus(locals.user.id); + return json(status); +}; +const POST = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const membership = await db.query.teamMember.findFirst({ + where: eq(teamMember.userId, locals.user.id), + with: { team: true } + }); + const personalTeam = membership?.team; + if (!personalTeam?.isPersonal) { + error(400, "No personal team found"); + } + try { + const result = await joinQueue(locals.user.id, personalTeam.id); + return json({ matched: !!result, match: result }); + } catch (e) { + error(400, e.message); + } +}; +const DELETE = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + await leaveQueue(locals.user.id); + return json({ ok: true }); +}; +export { + DELETE, + GET, + POST +}; diff --git a/.svelte-kit/adapter-node/entries/fallbacks/layout.svelte.js b/.svelte-kit/adapter-node/entries/fallbacks/layout.svelte.js new file mode 100644 index 0000000..c3792df --- /dev/null +++ b/.svelte-kit/adapter-node/entries/fallbacks/layout.svelte.js @@ -0,0 +1,9 @@ +import "clsx"; +function Layout($$renderer, $$props) { + let { children } = $$props; + children($$renderer); + $$renderer.push(``); +} +export { + Layout as default +}; diff --git a/.svelte-kit/adapter-node/entries/hooks.server.js b/.svelte-kit/adapter-node/entries/hooks.server.js new file mode 100644 index 0000000..35278f0 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/hooks.server.js @@ -0,0 +1,22 @@ +import { b as building } from "../chunks/environment.js"; +import { a as auth, s as svelteKitHandler } from "../chunks/auth.js"; +import { d as db, u as user } from "../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const handleBetterAuth = async ({ event, resolve }) => { + const session = await auth.api.getSession({ headers: event.request.headers }); + if (session) { + event.locals.session = session.session; + const dbUser = await db.query.user.findFirst({ + where: eq(user.id, session.user.id) + }); + event.locals.user = { + ...session.user, + role: dbUser?.role ?? "player" + }; + } + return svelteKitHandler({ event, resolve, auth, building }); +}; +const handle = handleBetterAuth; +export { + handle +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/_layout.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/_layout.server.ts.js new file mode 100644 index 0000000..18d72d1 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/_layout.server.ts.js @@ -0,0 +1,14 @@ +const load = async ({ locals }) => { + return { + user: locals.user ? { + id: locals.user.id, + name: locals.user.name, + email: locals.user.email, + image: locals.user.image, + role: locals.user.role ?? "player" + } : null + }; +}; +export { + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/_layout.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/_layout.svelte.js new file mode 100644 index 0000000..068b523 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/_layout.svelte.js @@ -0,0 +1,97 @@ +import { a as ensure_array_like, b as attr, c as attr_class, s as stringify, e as escape_html, d as derived } from "../../../chunks/index3.js"; +import { p as page } from "../../../chunks/index4.js"; +import "@sveltejs/kit/internal"; +import "../../../chunks/url.js"; +import "../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../chunks/root.js"; +import "../../../chunks/exports.js"; +import "../../../chunks/state.svelte.js"; +function Sidebar($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + const user = derived(() => page.data?.user); + const nav = [ + { href: "/", label: "Dashboard", icon: "⌂" }, + { href: "/matches", label: "Matches", icon: "⚔" }, + { href: "/mappools", label: "Mappools", icon: "♫" }, + { href: "/teams", label: "Teams", icon: "⚑" } + ]; + function isActive(href) { + if (href === "/") return page.url.pathname === "/"; + return page.url.pathname.startsWith(href); + } + const isAdmin = derived(() => user()?.role === "admin"); + $$renderer2.push(``); + }); +} +function Header($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + const user = derived(() => page.data?.user); + const roleBadge = { + referee: { label: "REF", color: "bg-yellow-500/20 text-yellow-400" }, + admin: { label: "ADMIN", color: "bg-red-500/20 text-red-400" } + }; + const badge = derived(() => user()?.role ? roleBadge[user().role] : null); + $$renderer2.push(`
`); + if (user()) { + $$renderer2.push(""); + $$renderer2.push(`
`); + if (badge()) { + $$renderer2.push(""); + $$renderer2.push(`${escape_html(badge().label)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (user().image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(user().name)}
`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Sign in with osu!`); + } + $$renderer2.push(`
`); + }); +} +function _layout($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { children } = $$props; + const user = derived(() => page.data?.user); + if (user()) { + $$renderer2.push(""); + $$renderer2.push(`
`); + Sidebar($$renderer2); + $$renderer2.push(`
`); + Header($$renderer2); + $$renderer2.push(`
`); + children($$renderer2); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + $$renderer2.push(`
V
Vash Esports
Sign in with osu!
`); + children($$renderer2); + $$renderer2.push(`
`); + } + $$renderer2.push(``); + }); +} +export { + _layout as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/_page.server.ts.js new file mode 100644 index 0000000..e04978c --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/_page.server.ts.js @@ -0,0 +1,39 @@ +import { d as db, b as match, c as team, m as mappool } from "../../../chunks/index2.js"; +import { desc, inArray, count, eq } from "drizzle-orm"; +const load = async ({ locals }) => { + if (!locals.user) { + return { + authenticated: false, + recentMatches: [], + liveMatches: [], + stats: { matches: 0, finished: 0, teams: 0, mappools: 0 } + }; + } + const recentMatches = await db.query.match.findMany({ + with: { participants: { with: { team: true } } }, + orderBy: desc(match.createdAt), + limit: 5 + }); + const liveMatches = await db.query.match.findMany({ + with: { participants: { with: { team: true } } }, + where: inArray(match.state, ["LOBBY", "ROLLING", "PICKING", "PLAYING"]) + }); + const [matchCount] = await db.select({ count: count() }).from(match); + const [teamCount] = await db.select({ count: count() }).from(team); + const [poolCount] = await db.select({ count: count() }).from(mappool); + const [finishedCount] = await db.select({ count: count() }).from(match).where(eq(match.state, "FINISHED")); + return { + authenticated: true, + recentMatches, + liveMatches, + stats: { + matches: matchCount.count, + finished: finishedCount.count, + teams: teamCount.count, + mappools: poolCount.count + } + }; +}; +export { + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/_page.svelte.js new file mode 100644 index 0000000..6f98657 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/_page.svelte.js @@ -0,0 +1,118 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify, c as attr_class } from "../../../chunks/index3.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const stateConfig = { + CREATED: { label: "Created", color: "text-text-secondary" }, + LOBBY: { label: "In Lobby", color: "text-yellow-400" }, + ROLLING: { + label: "Rolling", + color: "text-yellow-400", + dot: "bg-yellow-400" + }, + PICKING: { label: "Picking", color: "text-blue-400", dot: "bg-blue-400" }, + PLAYING: { label: "Live", color: "text-green-400", dot: "bg-green-400" }, + FINISHED: { label: "Finished", color: "text-text-secondary" }, + CANCELLED: { label: "Cancelled", color: "text-red-400" } + }; + let queueLoading = false; + function timeAgo(date) { + const d = new Date(date); + const diff = Date.now() - d.getTime(); + const mins = Math.floor(diff / 6e4); + if (mins < 1) return "just now"; + if (mins < 60) return `${mins}m ago`; + const hours = Math.floor(mins / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; + } + if (!data.user) { + $$renderer2.push(""); + $$renderer2.push(`
V

Welcome to Vash Esports

Automated tournament match management for osu! — lobbies, mappools, picks, bans, and + ELO tracking all in one place.

Sign in with osu!

Uses your osu! account. No extra registration needed.

`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

Welcome back, ${escape_html(data.user.name)}

Automated esports match management for osu!

${escape_html(data.stats.matches)}

Total Matches

${escape_html(data.stats.finished)}

Completed

${escape_html(data.stats.teams)}

Teams

${escape_html(data.stats.mappools)}

Mappools

`); + if (data.liveMatches.length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Live Now

`); + const each_array = ensure_array_like(data.liveMatches); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let m = each_array[$$index]; + const p1 = m.participants[0]; + const p2 = m.participants[1]; + const sc = stateConfig[m.state]; + $$renderer2.push(`
`); + if (p1?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(p1?.team.name ?? "?")}
${escape_html(p1?.score ?? 0)} vs ${escape_html(p2?.score ?? 0)}
${escape_html(p2?.team.name ?? "?")} `); + if (p2?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (sc?.dot) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label)}`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

Ranked Queue

Find a match at your skill level

`); + { + $$renderer2.push(""); + $$renderer2.push(`${escape_html("Find Match")}`); + } + $$renderer2.push(`

Recent Matches

`); + const each_array_1 = ensure_array_like(data.recentMatches); + if (each_array_1.length !== 0) { + $$renderer2.push(""); + for (let $$index_1 = 0, $$length = each_array_1.length; $$index_1 < $$length; $$index_1++) { + let m = each_array_1[$$index_1]; + const p1 = m.participants[0]; + const p2 = m.participants[1]; + const config = m.config; + const sc = stateConfig[m.state]; + $$renderer2.push(`
${escape_html(p1?.team.name ?? "?")} `); + if (m.state === "FINISHED") { + $$renderer2.push(""); + $$renderer2.push(` (p2?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p1?.score ?? 0)} - (p1?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p2?.score ?? 0)}`); + } else { + $$renderer2.push(""); + $$renderer2.push(`vs`); + } + $$renderer2.push(` ${escape_html(p2?.team.name ?? "?")}

${escape_html(m.name ? `${m.name} · ` : "")}BO${escape_html(config.bestOf)} + · ${escape_html(timeAgo(m.finishedAt ?? m.createdAt))}

`); + if (m.state === "FINISHED" && m.winnerId) { + $$renderer2.push(""); + const winner = m.participants.find((p) => p.teamId === m.winnerId); + $$renderer2.push(`🏆 ${escape_html(winner?.team.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label)}`); + } + } else { + $$renderer2.push(""); + $$renderer2.push(`

No matches yet. Create one

`); + } + $$renderer2.push(`
`); + } + $$renderer2.push(``); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/admin/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/admin/_page.server.ts.js new file mode 100644 index 0000000..7f5e1cf --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/admin/_page.server.ts.js @@ -0,0 +1,70 @@ +import { d as db, u as user, p as playerRating } from "../../../../chunks/index2.js"; +import { eq, desc } from "drizzle-orm"; +import { error } from "@sveltejs/kit"; +const ROLE_HIERARCHY = { + player: 0, + referee: 1, + admin: 2 +}; +function hasRole(userRole, minRole) { + const level = ROLE_HIERARCHY[userRole] ?? 0; + const required = ROLE_HIERARCHY[minRole]; + return level >= required; +} +function requireRole(locals, minRole, message = "Insufficient permissions") { + if (!locals.user) { + error(401, "Not logged in"); + } + if (!hasRole(locals.user.role, minRole)) { + error(403, message); + } +} +async function setUserRole(userId, role) { + await db.update(user).set({ role }).where(eq(user.id, userId)); +} +const load = async ({ locals }) => { + requireRole(locals, "admin"); + const users = await db.query.user.findMany({ + orderBy: desc(user.createdAt) + }); + const usersWithRatings = await Promise.all( + users.map(async (u) => { + const rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, u.id) + }); + return { + id: u.id, + name: u.name, + email: u.email, + image: u.image, + role: u.role ?? "player", + createdAt: u.createdAt, + elo: rating?.elo ?? 1e3, + wins: rating?.wins ?? 0, + losses: rating?.losses ?? 0 + }; + }) + ); + return { users: usersWithRatings }; +}; +const actions = { + setRole: async ({ request, locals }) => { + requireRole(locals, "admin"); + const form = await request.formData(); + const userId = form.get("userId")?.toString(); + const role = form.get("role")?.toString(); + if (!userId || !role) return { error: "User ID and role are required" }; + if (!["player", "referee", "admin"].includes(role)) { + return { error: "Invalid role" }; + } + if (userId === locals.user.id && role !== "admin") { + return { error: "You can't demote yourself" }; + } + await setUserRole(userId, role); + return { success: true }; + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/admin/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/admin/_page.svelte.js new file mode 100644 index 0000000..59b9970 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/admin/_page.svelte.js @@ -0,0 +1,70 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const roleBadge = { + player: { + label: "Player", + color: "bg-surface-600 text-text-secondary border-border" + }, + referee: { + label: "Referee", + color: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30" + }, + admin: { + label: "Admin", + color: "bg-red-500/20 text-red-400 border-red-500/30" + } + }; + $$renderer2.push(`

Admin Panel

${escape_html(data.users.length)} registered user${escape_html(data.users.length !== 1 ? "s" : "")}

`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
Avatar User ELO W/L Role
`); + const each_array = ensure_array_like(data.users); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let u = each_array[$$index]; + const badge = roleBadge[u.role] ?? roleBadge.player; + $$renderer2.push(`
`); + if (u.image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
${escape_html(u.name.charAt(0).toUpperCase())}
`); + } + $$renderer2.push(`

${escape_html(u.name)}

${escape_html(u.email)}

${escape_html(u.elo)} ${escape_html(u.wins)}W / ${escape_html(u.losses)}L
`); + $$renderer2.select( + { + name: "role", + value: u.role, + onchange: (e) => e.currentTarget.form?.requestSubmit(), + class: `rounded-md border px-2 py-1 text-xs font-500 ${stringify(badge.color)} cursor-pointer bg-transparent focus:outline-none` + }, + ($$renderer3) => { + $$renderer3.option({ value: "player", class: "bg-surface-800 text-text-primary" }, ($$renderer4) => { + $$renderer4.push(`Player`); + }); + $$renderer3.option({ value: "referee", class: "bg-surface-800 text-text-primary" }, ($$renderer4) => { + $$renderer4.push(`Referee`); + }); + $$renderer3.option({ value: "admin", class: "bg-surface-800 text-text-primary" }, ($$renderer4) => { + $$renderer4.push(`Admin`); + }); + } + ); + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_id_/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_id_/_page.server.ts.js new file mode 100644 index 0000000..cbdf47b --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_id_/_page.server.ts.js @@ -0,0 +1,100 @@ +import { d as db, m as mappool, a as mappoolSlot } from "../../../../../chunks/index2.js"; +import { eq, and } from "drizzle-orm"; +import { redirect, error } from "@sveltejs/kit"; +import { g as getBeatmap } from "../../../../../chunks/api.js"; +const load = async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + const pool = await db.query.mappool.findFirst({ + where: eq(mappool.id, params.id), + with: { + slots: { + orderBy: (s, { asc }) => [asc(s.category), asc(s.orderInCategory)] + } + } + }); + if (!pool) error(404, "Mappool not found"); + const slotsWithBeatmaps = await Promise.all( + pool.slots.map(async (slot) => { + try { + const beatmap = await getBeatmap(slot.beatmapId); + return { + ...slot, + beatmap: { + title: beatmap.beatmapset.title, + artist: beatmap.beatmapset.artist, + version: beatmap.version, + starRating: beatmap.difficulty_rating, + bpm: beatmap.bpm, + totalLength: beatmap.total_length, + coverUrl: beatmap.beatmapset.covers["card@2x"], + url: beatmap.url + } + }; + } catch { + return { + ...slot, + beatmap: null + }; + } + }) + ); + return { + pool: { ...pool, slots: slotsWithBeatmaps } + }; +}; +const actions = { + addSlot: async ({ params, request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + let beatmapId = form.get("beatmapId")?.toString()?.trim(); + const category = form.get("category")?.toString()?.trim()?.toUpperCase(); + if (!beatmapId || !category) { + return { error: "Beatmap ID and category are required" }; + } + const urlMatch = beatmapId.match(/beatmaps\/(\d+)/); + const setMatch = beatmapId.match(/beatmapsets\/\d+#\w+\/(\d+)/); + if (urlMatch) beatmapId = urlMatch[1]; + else if (setMatch) beatmapId = setMatch[1]; + if (!/^\d+$/.test(beatmapId)) { + return { error: "Invalid beatmap ID" }; + } + let beatmap; + try { + beatmap = await getBeatmap(beatmapId); + } catch { + return { error: "Beatmap not found on osu!" }; + } + const existing = await db.query.mappoolSlot.findMany({ + where: and( + eq(mappoolSlot.mappoolId, params.id), + eq(mappoolSlot.category, category) + ) + }); + await db.insert(mappoolSlot).values({ + mappoolId: params.id, + beatmapId, + category, + orderInCategory: existing.length + 1, + starRating: beatmap.difficulty_rating, + mods: category === "NM" || category === "TB" || category === "FM" ? [] : [category] + }); + return { success: true }; + }, + removeSlot: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const slotId = form.get("slotId")?.toString(); + if (!slotId) return { error: "Missing slot ID" }; + await db.delete(mappoolSlot).where(eq(mappoolSlot.id, slotId)); + return { success: true }; + }, + deletePool: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + await db.delete(mappool).where(eq(mappool.id, params.id)); + redirect(303, "/mappools"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_id_/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_id_/_page.svelte.js new file mode 100644 index 0000000..0a8825e --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_id_/_page.svelte.js @@ -0,0 +1,98 @@ +import { e as escape_html, a as ensure_array_like, b as attr, c as attr_class, d as derived, s as stringify } from "../../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../../chunks/url.js"; +import "../../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../../chunks/root.js"; +import "../../../../../chunks/exports.js"; +import "../../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const categories = ["NM", "HD", "HR", "DT", "FM", "TB"]; + let addingCategory = "NM"; + let beatmapInput = ""; + const grouped = derived(() => () => { + const groups = {}; + for (const cat of categories) { + const slots = data.pool.slots.filter((s) => s.category === cat); + if (slots.length > 0) groups[cat] = slots; + } + return groups; + }); + function formatLength(seconds) { + const m = Math.floor(seconds / 60); + const s = seconds % 60; + return `${m}:${s.toString().padStart(2, "0")}`; + } + const categoryColors = { + NM: "bg-blue-500/20 text-blue-400 border-blue-500/30", + HD: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", + HR: "bg-red-500/20 text-red-400 border-red-500/30", + DT: "bg-purple-500/20 text-purple-400 border-purple-500/30", + FM: "bg-green-500/20 text-green-400 border-green-500/30", + TB: "bg-pink-500/20 text-pink-400 border-pink-500/30" + }; + $$renderer2.push(`
←

${escape_html(data.pool.name)}

${escape_html(data.pool.slots.length)} map${escape_html(data.pool.slots.length !== 1 ? "s" : "")}

Add Map

`); + $$renderer2.select( + { + name: "category", + value: addingCategory, + class: "rounded-md border border-border bg-surface-700 px-3 py-2 text-sm text-text-primary focus:border-accent focus:outline-none" + }, + ($$renderer3) => { + $$renderer3.push(``); + const each_array = ensure_array_like(categories); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let cat = each_array[$$index]; + $$renderer3.option({ value: cat }, ($$renderer4) => { + $$renderer4.push(`${escape_html(cat)}`); + }); + } + $$renderer3.push(``); + } + ); + $$renderer2.push(`
`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array_1 = ensure_array_like(Object.entries(grouped()())); + if (each_array_1.length !== 0) { + $$renderer2.push(""); + for (let $$index_2 = 0, $$length = each_array_1.length; $$index_2 < $$length; $$index_2++) { + let [category, slots] = each_array_1[$$index_2]; + $$renderer2.push(`
${escape_html(category)} ${escape_html(slots.length)} map${escape_html(slots.length !== 1 ? "s" : "")}
`); + const each_array_2 = ensure_array_like(slots); + for (let i = 0, $$length2 = each_array_2.length; i < $$length2; i++) { + let slot = each_array_2[i]; + $$renderer2.push(`
`); + if (slot.beatmap?.coverUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
No cover
`); + } + $$renderer2.push(`
`); + if (slot.beatmap) { + $$renderer2.push(""); + $$renderer2.push(`${escape_html(slot.beatmap.artist)} - ${escape_html(slot.beatmap.title)}
[${escape_html(slot.beatmap.version)}] ★ ${escape_html(slot.beatmap.starRating.toFixed(2))} ${escape_html(slot.beatmap.bpm)} BPM ${escape_html(formatLength(slot.beatmap.totalLength))}
`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

Beatmap #${escape_html(slot.beatmapId)}

`); + } + $$renderer2.push(`
${escape_html(category)}${escape_html(i + 1)}
`); + } + $$renderer2.push(`
`); + } + } else { + $$renderer2.push(""); + $$renderer2.push(`

No maps yet. Add your first map above.

`); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_page.server.ts.js new file mode 100644 index 0000000..77c98ad --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_page.server.ts.js @@ -0,0 +1,24 @@ +import { d as db, m as mappool } from "../../../../chunks/index2.js"; +import { redirect } from "@sveltejs/kit"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const mappools = await db.query.mappool.findMany({ + with: { slots: true }, + orderBy: (m, { desc }) => [desc(m.createdAt)] + }); + return { mappools }; +}; +const actions = { + create: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const name = form.get("name")?.toString()?.trim(); + if (!name) return { error: "Name is required" }; + const [created] = await db.insert(mappool).values({ name, createdBy: locals.user.id }).returning(); + redirect(303, `/mappools/${created.id}`); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_page.svelte.js new file mode 100644 index 0000000..c122aec --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/mappools/_page.svelte.js @@ -0,0 +1,34 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + $$renderer2.push(`

Mappools

Create and manage your beatmap pools

`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array = ensure_array_like(data.mappools); + if (each_array.length !== 0) { + $$renderer2.push(""); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let pool = each_array[$$index]; + $$renderer2.push(`

${escape_html(pool.name)}

${escape_html(pool.slots.length)} map${escape_html(pool.slots.length !== 1 ? "s" : "")} + · Created ${escape_html(new Date(pool.createdAt).toLocaleDateString())}

→`); + } + } else { + $$renderer2.push(""); + $$renderer2.push(`

No mappools yet. Create one to get started.

`); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/matches/_id_/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_id_/_page.server.ts.js new file mode 100644 index 0000000..497a952 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_id_/_page.server.ts.js @@ -0,0 +1,129 @@ +import { d as db, u as user } from "../../../../../chunks/index2.js"; +import { a as cancelMatch, g as getMatchFull, p as pickMap, s as submitRoll } from "../../../../../chunks/engine.js"; +import { c as closeLobby, f as forceStartGame, p as playPickedMap, g as getLobby } from "../../../../../chunks/orchestrator.js"; +import { redirect, error } from "@sveltejs/kit"; +import { g as getBeatmap } from "../../../../../chunks/api.js"; +import { eq } from "drizzle-orm"; +const load = async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + let m; + try { + m = await getMatchFull(params.id); + } catch { + error(404, "Match not found"); + } + const beatmapCache = {}; + if (m.mappool?.slots) { + await Promise.allSettled( + m.mappool.slots.map(async (slot) => { + try { + const bm = await getBeatmap(slot.beatmapId); + beatmapCache[slot.beatmapId] = { + title: bm.beatmapset.title, + artist: bm.beatmapset.artist, + version: bm.version, + starRating: bm.difficulty_rating, + bpm: bm.bpm, + totalLength: bm.total_length, + coverUrl: bm.beatmapset.covers["card@2x"], + listCoverUrl: bm.beatmapset.covers["list@2x"] + }; + } catch { + } + }) + ); + } + return { match: m, beatmapCache, userId: locals.user.id }; +}; +const actions = { + reinvite: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + const lobby = getLobby(params.id); + if (!lobby) return { error: "No active IRC lobby for this match" }; + const m = await getMatchFull(params.id); + const invited = []; + for (const p of m.participants) { + for (const pl of p.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name) { + await lobby.invite(u.name); + invited.push(u.name); + } + } + } + return { reinvited: invited }; + }, + roll: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + const m = await getMatchFull(params.id); + let myParticipant = m.participants.find( + (p) => p.players.some((pl) => pl.userId === locals.user.id) + ); + if (!myParticipant) { + return { error: "You are not in this match" }; + } + if (myParticipant.rollValue !== null) { + const unrolled = m.participants.find((p) => p.rollValue === null); + if (unrolled) { + myParticipant = unrolled; + } else { + return { error: "All participants have already rolled" }; + } + } + const value = Math.floor(Math.random() * 100) + 1; + await submitRoll(params.id, myParticipant.id, value); + const lobby = getLobby(params.id); + if (lobby) { + lobby.chat(`${myParticipant.team?.name ?? "Player"} rolled ${value} (via web)`).catch(() => { + }); + const updated = await getMatchFull(params.id); + if (updated.state === "PICKING") { + const sorted = [...updated.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + lobby.chat( + `Rolls complete! ${sorted[0]?.team.name} picks first. Use !pick (e.g. !pick NM1) or pick in web UI.` + ).catch(() => { + }); + } + } + return { rolled: value, participantId: myParticipant.id }; + }, + pick: async ({ params, request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const slotId = form.get("slotId")?.toString(); + if (!slotId) return { error: "No map selected" }; + const m = await getMatchFull(params.id); + const sorted = [...m.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + const expectedIdx = m.games.length % sorted.length; + const expectedPicker = sorted[expectedIdx]; + if (!expectedPicker) return { error: "Cannot determine picker" }; + try { + const game = await pickMap(params.id, expectedPicker.id, slotId); + playPickedMap(params.id, game.id).catch( + (err) => console.error("[Match] IRC play failed:", err.message) + ); + return { picked: true, gameId: game.id }; + } catch (e) { + return { error: e.message }; + } + }, + forceStart: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + await forceStartGame(params.id); + return { started: true }; + }, + cancel: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + await cancelMatch(params.id); + await closeLobby(params.id); + redirect(303, "/matches"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/matches/_id_/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_id_/_page.svelte.js new file mode 100644 index 0000000..e6b854f --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_id_/_page.svelte.js @@ -0,0 +1,323 @@ +import { e as escape_html, b as attr, s as stringify, c as attr_class, a as ensure_array_like, d as derived } from "../../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../../chunks/url.js"; +import "../../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../../chunks/root.js"; +import "../../../../../chunks/exports.js"; +import "../../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + let m = derived(() => data.match); + let p1 = derived(() => m().participants[0]); + let p2 = derived(() => m().participants[1]); + const config = derived(() => m().config); + const winsNeeded = derived(() => Math.ceil(config().bestOf / 2)); + const playedSlotIds = derived(() => new Set(m().games.map((g) => g.mappoolSlotId))); + const sortedByPick = derived(() => [...m().participants].sort((a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99))); + const expectedPickerIdx = derived(() => m().games.length % sortedByPick().length); + const expectedPicker = derived(() => sortedByPick()[expectedPickerIdx()]); + const canRoll = derived(() => m().state === "ROLLING" && m().participants.some((p) => p.rollValue === null)); + const catColors = { + NM: "bg-blue-500/20 text-blue-400 border-blue-500/30", + HD: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", + HR: "bg-red-500/20 text-red-400 border-red-500/30", + DT: "bg-purple-500/20 text-purple-400 border-purple-500/30", + FM: "bg-green-500/20 text-green-400 border-green-500/30", + TB: "bg-pink-500/20 text-pink-400 border-pink-500/30" + }; + const groupedSlots = derived(() => () => { + if (!m().mappool?.slots) return {}; + const groups = {}; + for (const slot of m().mappool.slots) { + if (!groups[slot.category]) groups[slot.category] = []; + groups[slot.category].push(slot); + } + for (const cat of Object.keys(groups)) { + groups[cat].sort((a, b) => a.orderInCategory - b.orderInCategory); + } + return groups; + }); + let rolling = false; + let picking = false; + let forceStarting = false; + function formatLength(seconds) { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, "0")}`; + } + function stateLabel(s) { + return { + CREATED: "Waiting", + LOBBY: "In Lobby", + ROLLING: "Rolling", + PICKING: "Pick Phase", + PLAYING: "Playing", + FINISHED: "Finished", + CANCELLED: "Cancelled" + }[s] ?? s; + } + function stateBorder(s) { + return { + ROLLING: "border-yellow-500/30", + PICKING: "border-blue-500/30", + PLAYING: "border-green-500/30", + FINISHED: "border-accent/30" + }[s] ?? "border-border"; + } + $$renderer2.push(`
←

${escape_html(m().name || "Match")}

Best of ${escape_html(config().bestOf)} · First to ${escape_html(winsNeeded())} `); + if (m().osuLobbyId) { + $$renderer2.push(""); + $$renderer2.push(`· osu! mp/${escape_html(m().osuLobbyId)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

${escape_html(stateLabel(m().state))} `); + if (!["FINISHED", "CANCELLED"].includes(m().state)) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (p1() && p2()) { + $$renderer2.push(""); + $$renderer2.push(`
`); + if (p1().team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

${escape_html(p1().team.name)}

`); + if (p1().rollValue != null) { + $$renderer2.push(""); + $$renderer2.push(`

Roll: ${escape_html(p1().rollValue)}${escape_html(p1().pickOrder === 1 ? " ★" : "")}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
= winsNeeded() ? "text-green-400" : "text-text-primary")}`)}>${escape_html(p1().score)}
vs = winsNeeded() ? "text-green-400" : "text-text-primary")}`)}>${escape_html(p2().score)}

${escape_html(p2().team.name)}

`); + if (p2().rollValue != null) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(p2().pickOrder === 1 ? "★ " : "")}Roll: ${escape_html(p2().rollValue)}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (p2().team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (["LOBBY", "ROLLING", "PICKING", "PLAYING"].includes(m().state)) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "ROLLING") { + $$renderer2.push(""); + $$renderer2.push(`

🎲 Roll Phase

Highest roll picks first

`); + const each_array = ensure_array_like(m().participants); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let p = each_array[$$index]; + $$renderer2.push(`

${escape_html(p.team.name)}

`); + if (p.rollValue != null) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(p.rollValue)}

`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

—

`); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + if (canRoll()) { + $$renderer2.push(""); + $$renderer2.push(`

or type !roll in osu! chat

`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

Waiting for all rolls...

`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "PICKING") { + $$renderer2.push(""); + $$renderer2.push(`

🎯 Pick a Map

${escape_html(expectedPicker()?.team.name)}'s turn to pick

or type !pick NM1 in osu! chat

`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array_1 = ensure_array_like(Object.entries(groupedSlots()())); + for (let $$index_2 = 0, $$length = each_array_1.length; $$index_2 < $$length; $$index_2++) { + let [category, slots] = each_array_1[$$index_2]; + $$renderer2.push(`
${escape_html(category)}
`); + const each_array_2 = ensure_array_like(slots); + for (let i = 0, $$length2 = each_array_2.length; i < $$length2; i++) { + let slot = each_array_2[i]; + const bm = data.beatmapCache[slot.beatmapId]; + const isPlayed = playedSlotIds().has(slot.id); + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "PLAYING") { + $$renderer2.push(""); + const currentGame = m().games[m().games.length - 1]; + const bm = currentGame ? data.beatmapCache[currentGame.slot?.beatmapId] : null; + $$renderer2.push(`
Now Playing
`); + if (bm) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(bm.artist)} - ${escape_html(bm.title)} [${escape_html(bm.version)}]

★${escape_html(bm.starRating.toFixed(1))} · ${escape_html(bm.bpm)}bpm · ${escape_html(formatLength(bm.totalLength))}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

Waiting for players to ready up & play in osu!...

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "FINISHED") { + $$renderer2.push(""); + const winner = m().participants.find((p) => p.teamId === m().winnerId); + $$renderer2.push(`
🏆

${escape_html(winner?.team.name)} wins!

${escape_html(p1()?.score)} – ${escape_html(p2()?.score)}

`); + const each_array_3 = ensure_array_like(m().games); + for (let $$index_3 = 0, $$length = each_array_3.length; $$index_3 < $$length; $$index_3++) { + let game = each_array_3[$$index_3]; + const bm = data.beatmapCache[game.slot?.beatmapId]; + const winnerP = m().participants.find((p) => p.id === game.winnerParticipantId); + $$renderer2.push(`
${escape_html(game.slot?.category)}${escape_html(game.slot?.orderInCategory)} ${escape_html(bm ? `${bm.artist} - ${bm.title}` : `#${game.slot?.beatmapId}`)} ${escape_html(winnerP?.team.name)}
`); + } + $$renderer2.push(`
Back to Dashboard
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().games.length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Games Played

`); + const each_array_4 = ensure_array_like(m().games); + for (let $$index_4 = 0, $$length = each_array_4.length; $$index_4 < $$length; $$index_4++) { + let game = each_array_4[$$index_4]; + const bm = data.beatmapCache[game.slot?.beatmapId]; + const pickerTeam = m().participants.find((p) => p.id === game.pickedByParticipantId)?.team; + const winnerTeam = m().participants.find((p) => p.id === game.winnerParticipantId)?.team; + $$renderer2.push(`
${escape_html(game.gameNumber)} `); + if (bm?.listCoverUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (bm) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(bm.artist)} - ${escape_html(bm.title)} [${escape_html(bm.version)}]

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
${escape_html(game.slot?.category)}${escape_html(game.slot?.orderInCategory)} `); + if (pickerTeam) { + $$renderer2.push(""); + $$renderer2.push(`Picked by ${escape_html(pickerTeam.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (game.state === "FINISHED" && p1() && p2()) { + $$renderer2.push(""); + const p1Score = game.scores.filter((s) => s.player?.participantId === p1().id).reduce((sum, s) => sum + s.score, 0); + const p2Score = game.scores.filter((s) => s.player?.participantId === p2().id).reduce((sum, s) => sum + s.score, 0); + $$renderer2.push(`
p2Score ? "font-700 text-green-400" : "text-text-secondary")}>${escape_html(p1Score.toLocaleString())} - p1Score ? "font-700 text-green-400" : "text-text-secondary")}>${escape_html(p2Score.toLocaleString())}
`); + } else if (game.state === "PLAYING") { + $$renderer2.push(""); + $$renderer2.push(`Live`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (winnerTeam) { + $$renderer2.push(""); + $$renderer2.push(`✓ ${escape_html(winnerTeam.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "CREATED") { + $$renderer2.push(""); + $$renderer2.push(`

Creating osu! lobby...

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "LOBBY") { + $$renderer2.push(""); + $$renderer2.push(`

Lobby created. Waiting for players to join...

`); + if (m().osuLobbyId) { + $$renderer2.push(""); + $$renderer2.push(`

osu! mp/${escape_html(m().osuLobbyId)}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "CANCELLED") { + $$renderer2.push(""); + $$renderer2.push(`

This match was cancelled.

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(``); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/matches/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_page.server.ts.js new file mode 100644 index 0000000..2891ac2 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_page.server.ts.js @@ -0,0 +1,58 @@ +import { d as db, b as match } from "../../../../chunks/index2.js"; +import { redirect } from "@sveltejs/kit"; +import { desc } from "drizzle-orm"; +import { c as createMatch } from "../../../../chunks/engine.js"; +import { i as initMatchLobby } from "../../../../chunks/orchestrator.js"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const matches = await db.query.match.findMany({ + with: { participants: { with: { team: true } } }, + orderBy: desc(match.createdAt), + limit: 50 + }); + const teams = await db.query.team.findMany({ + orderBy: (t, { asc }) => [asc(t.name)] + }); + const mappools = await db.query.mappool.findMany({ + with: { slots: true }, + orderBy: (m, { desc: desc2 }) => [desc2(m.createdAt)] + }); + return { matches, teams, mappools }; +}; +const actions = { + createMatch: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const name = form.get("name")?.toString()?.trim() || "Custom Match"; + const team1Id = form.get("team1")?.toString(); + const team2Id = form.get("team2")?.toString(); + const mappoolId = form.get("mappool")?.toString(); + const bestOf = parseInt(form.get("bestOf")?.toString() ?? "7"); + console.log("[Matches] CREATE:", { name, team1Id, team2Id, mappoolId, bestOf }); + if (!team1Id || !team2Id || !mappoolId) { + return { error: "All fields are required" }; + } + let result; + try { + result = await createMatch({ + name, + config: { bestOf, teamSize: 1, scoringType: "score" }, + mappoolId, + teams: [team1Id, team2Id], + createdBy: locals.user.id + }); + console.log("[Matches] Created:", result.id); + } catch (e) { + console.error("[Matches] Error:", e); + return { error: e.message }; + } + initMatchLobby(result.id).catch((err) => { + console.error("[Matches] IRC lobby failed:", err.message); + }); + redirect(303, `/matches/${result.id}`); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/matches/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_page.svelte.js new file mode 100644 index 0000000..9af67ae --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/matches/_page.svelte.js @@ -0,0 +1,164 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify, c as attr_class, d as derived } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const stateConfig = { + CREATED: { label: "Created", color: "text-text-secondary border-border" }, + LOBBY: { + label: "In Lobby", + color: "text-yellow-400 border-yellow-500/30" + }, + ROLLING: { + label: "Rolling", + color: "text-yellow-400 border-yellow-500/30", + dot: "bg-yellow-400" + }, + PICKING: { + label: "Picking", + color: "text-blue-400 border-blue-500/30", + dot: "bg-blue-400" + }, + PLAYING: { + label: "Live", + color: "text-green-400 border-green-500/30", + dot: "bg-green-400" + }, + FINISHED: { + label: "Finished", + color: "text-text-secondary border-border" + }, + CANCELLED: { label: "Cancelled", color: "text-red-400 border-red-500/30" } + }; + const liveStates = ["LOBBY", "ROLLING", "PICKING", "PLAYING"]; + const liveMatches = derived(() => data.matches.filter((m) => liveStates.includes(m.state))); + const recentMatches = derived(() => data.matches.filter((m) => !liveStates.includes(m.state))); + function teamDisplay(m) { + const p1 = m.participants[0]; + const p2 = m.participants[1]; + return { p1, p2 }; + } + function timeAgo(date) { + const d = new Date(date); + const diff = Date.now() - d.getTime(); + const mins = Math.floor(diff / 6e4); + if (mins < 1) return "just now"; + if (mins < 60) return `${mins}m ago`; + const hours = Math.floor(mins / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; + } + $$renderer2.push(`

Matches

${escape_html(data.matches.length)} match${escape_html(data.matches.length !== 1 ? "es" : "")} `); + if (liveMatches().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`· ${escape_html(liveMatches().length)} live`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

`); + { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (liveMatches().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Live Now

`); + const each_array_4 = ensure_array_like(liveMatches()); + for (let $$index_4 = 0, $$length = each_array_4.length; $$index_4 < $$length; $$index_4++) { + let m = each_array_4[$$index_4]; + const { p1, p2 } = teamDisplay(m); + const sc = stateConfig[m.state]; + $$renderer2.push(`
`); + if (p1?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(p1?.team.name ?? "?")}
${escape_html(p1?.score ?? 0)} vs ${escape_html(p2?.score ?? 0)}
${escape_html(p2?.team.name ?? "?")} `); + if (p2?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (sc?.dot) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label ?? m.state)}`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (liveMatches().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Past Matches

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array_5 = ensure_array_like(recentMatches()); + if (each_array_5.length !== 0) { + $$renderer2.push(""); + for (let $$index_5 = 0, $$length = each_array_5.length; $$index_5 < $$length; $$index_5++) { + let m = each_array_5[$$index_5]; + const { p1, p2 } = teamDisplay(m); + const config = m.config; + const sc = stateConfig[m.state]; + $$renderer2.push(`
${escape_html(p1?.team.name ?? "?")} `); + if (m.state === "FINISHED") { + $$renderer2.push(""); + $$renderer2.push(` (p2?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p1?.score ?? 0)} - (p1?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p2?.score ?? 0)}`); + } else { + $$renderer2.push(""); + $$renderer2.push(`vs`); + } + $$renderer2.push(` ${escape_html(p2?.team.name ?? "?")}

${escape_html(m.name ? `${m.name} · ` : "")}BO${escape_html(config.bestOf)} `); + if (m.finishedAt) { + $$renderer2.push(""); + $$renderer2.push(`· ${escape_html(timeAgo(m.finishedAt))}`); + } else if (m.createdAt) { + $$renderer2.push(""); + $$renderer2.push(`· ${escape_html(timeAgo(m.createdAt))}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

`); + if (m.state === "FINISHED" && m.winnerId) { + $$renderer2.push(""); + const winner = m.participants.find((p) => p.teamId === m.winnerId); + $$renderer2.push(`🏆 ${escape_html(winner?.team.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label ?? m.state)}`); + } + } else { + $$renderer2.push(""); + if (liveMatches().length === 0) { + $$renderer2.push(""); + $$renderer2.push(`

No matches yet. Create one to get started.

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(``); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/settings/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/settings/_page.server.ts.js new file mode 100644 index 0000000..0198d65 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/settings/_page.server.ts.js @@ -0,0 +1,37 @@ +import { redirect } from "@sveltejs/kit"; +import { a as auth } from "../../../../chunks/auth.js"; +import { d as db, p as playerRating, j as account } from "../../../../chunks/index2.js"; +import { eq, and } from "drizzle-orm"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, locals.user.id) + }); + const osuAccount = await db.query.account.findFirst({ + where: and(eq(account.userId, locals.user.id), eq(account.providerId, "osu")) + }); + return { + profile: { + id: locals.user.id, + name: locals.user.name, + email: locals.user.email, + image: locals.user.image, + role: locals.user.role ?? "player", + createdAt: locals.user.createdAt + }, + rating: rating ? { elo: rating.elo, wins: rating.wins, losses: rating.losses } : { elo: 1e3, wins: 0, losses: 0 }, + hasOsuLinked: !!osuAccount + }; +}; +const actions = { + logout: async (event) => { + await auth.api.signOut({ + headers: event.request.headers + }); + redirect(302, "/"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/settings/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/settings/_page.svelte.js new file mode 100644 index 0000000..3b24951 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/settings/_page.svelte.js @@ -0,0 +1,48 @@ +import { b as attr, e as escape_html, c as attr_class, s as stringify, d as derived } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const roleBadge = { + player: { label: "Player", color: "bg-surface-600 text-text-secondary" }, + referee: { label: "Referee", color: "bg-yellow-500/20 text-yellow-400" }, + admin: { label: "Admin", color: "bg-red-500/20 text-red-400" } + }; + const badge = derived(() => roleBadge[data.profile.role] ?? roleBadge.player); + const winRate = derived(() => data.rating.wins + data.rating.losses > 0 ? (data.rating.wins / (data.rating.wins + data.rating.losses) * 100).toFixed(1) : "—"); + $$renderer2.push(`

Settings

Your profile and account settings

`); + if (data.profile.image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
${escape_html(data.profile.name.charAt(0).toUpperCase())}
`); + } + $$renderer2.push(`

${escape_html(data.profile.name)}

${escape_html(badge().label)}

${escape_html(data.profile.email)}

Connected Accounts

🎮

osu!

`); + if (data.hasOsuLinked) { + $$renderer2.push(""); + $$renderer2.push(`Connected as ${escape_html(data.profile.name)}`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Not connected`); + } + $$renderer2.push(`

`); + if (data.hasOsuLinked) { + $$renderer2.push(""); + $$renderer2.push(`Linked`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Connect`); + } + $$renderer2.push(`

Ranked Stats

${escape_html(data.rating.elo)}

ELO Rating

${escape_html(data.rating.wins)}

Wins

${escape_html(data.rating.losses)}

Losses

${escape_html(winRate())}${escape_html(winRate() !== "—" ? "%" : "")}

Win Rate

Account

`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/teams/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/(app)/teams/_page.server.ts.js new file mode 100644 index 0000000..dbeb11a --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/teams/_page.server.ts.js @@ -0,0 +1,88 @@ +import { d as db, c as team, t as teamMember, u as user } from "../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +import { redirect } from "@sveltejs/kit"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const teams = await db.query.team.findMany({ + with: { + members: true + }, + orderBy: (t, { desc }) => [desc(t.createdAt)] + }); + const teamsWithUsers = await Promise.all( + teams.map(async (t) => { + const membersWithUsers = await Promise.all( + t.members.map(async (m) => { + const u = await db.query.user.findFirst({ + where: eq(user.id, m.userId) + }); + return { ...m, user: u ? { id: u.id, name: u.name, image: u.image } : null }; + }) + ); + return { ...t, members: membersWithUsers }; + }) + ); + return { teams: teamsWithUsers, userId: locals.user.id }; +}; +const actions = { + create: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const name = form.get("name")?.toString()?.trim(); + if (!name) return { error: "Team name is required" }; + const [created] = await db.insert(team).values({ + name, + ownerId: locals.user.id, + isPersonal: false + }).returning(); + await db.insert(teamMember).values({ + teamId: created.id, + userId: locals.user.id, + role: "captain" + }); + return { success: true }; + }, + addMember: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const teamId = form.get("teamId")?.toString(); + const username = form.get("username")?.toString()?.trim(); + if (!teamId || !username) return { error: "Team and username are required" }; + const u = await db.query.user.findFirst({ + where: eq(user.name, username) + }); + if (!u) return { error: `User "${username}" not found. They need to log in first.` }; + const existing = await db.query.teamMember.findFirst({ + where: (m, { and, eq: eq2 }) => and(eq2(m.teamId, teamId), eq2(m.userId, u.id)) + }); + if (existing) return { error: `${username} is already on this team` }; + await db.insert(teamMember).values({ + teamId, + userId: u.id, + role: "member" + }); + return { success: true }; + }, + removeMember: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const memberId = form.get("memberId")?.toString(); + if (!memberId) return { error: "Missing member ID" }; + await db.delete(teamMember).where(eq(teamMember.id, memberId)); + return { success: true }; + }, + deleteTeam: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const teamId = form.get("teamId")?.toString(); + if (!teamId) return { error: "Missing team ID" }; + const t = await db.query.team.findFirst({ where: eq(team.id, teamId) }); + if (!t || t.ownerId !== locals.user.id) return { error: "Not authorized" }; + await db.delete(team).where(eq(team.id, teamId)); + return { success: true }; + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/(app)/teams/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/(app)/teams/_page.svelte.js new file mode 100644 index 0000000..e5203aa --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/(app)/teams/_page.svelte.js @@ -0,0 +1,138 @@ +import { e as escape_html, a as ensure_array_like, b as attr, c as attr_class, s as stringify, d as derived } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + let expandedTeam = null; + let addingMemberTo = null; + let memberUsername = ""; + const myTeams = derived(() => data.teams.filter((t) => t.members.some((m) => m.userId === data.userId))); + const otherTeams = derived(() => data.teams.filter((t) => !t.members.some((m) => m.userId === data.userId))); + function isOwner(t) { + return t.ownerId === data.userId; + } + $$renderer2.push(`

Teams

${escape_html(data.teams.length)} team${escape_html(data.teams.length !== 1 ? "s" : "")}

`); + { + $$renderer2.push(""); + } + $$renderer2.push(` `); + { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (myTeams().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

My Teams

`); + const each_array = ensure_array_like(myTeams()); + for (let $$index_1 = 0, $$length = each_array.length; $$index_1 < $$length; $$index_1++) { + let t = each_array[$$index_1]; + $$renderer2.push(`
`); + if (expandedTeam === t.id) { + $$renderer2.push(""); + $$renderer2.push(`
`); + const each_array_1 = ensure_array_like(t.members); + for (let $$index = 0, $$length2 = each_array_1.length; $$index < $$length2; $$index++) { + let member = each_array_1[$$index]; + $$renderer2.push(`
`); + if (member.user?.image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
`); + } + $$renderer2.push(` ${escape_html(member.user?.name ?? "Unknown")} ${escape_html(member.role)} `); + if (isOwner(t) && member.userId !== data.userId) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + if (isOwner(t) && !t.isPersonal) { + $$renderer2.push(""); + if (addingMemberTo === t.id) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + $$renderer2.push(``); + } + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (isOwner(t) && !t.isPersonal) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (otherTeams().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Other Teams

`); + const each_array_2 = ensure_array_like(otherTeams()); + for (let $$index_2 = 0, $$length = each_array_2.length; $$index_2 < $$length; $$index_2++) { + let t = each_array_2[$$index_2]; + $$renderer2.push(`
`); + if (t.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
${escape_html(t.name.charAt(0).toUpperCase())}
`); + } + $$renderer2.push(`
${escape_html(t.name)}

${escape_html(t.members.length)} member${escape_html(t.members.length !== 1 ? "s" : "")}

`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (data.teams.length === 0) { + $$renderer2.push(""); + $$renderer2.push(`

No teams yet. Your personal team is created when you sign in.

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/_error.svelte.js b/.svelte-kit/adapter-node/entries/pages/_error.svelte.js new file mode 100644 index 0000000..85b4f7d --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/_error.svelte.js @@ -0,0 +1,25 @@ +import { e as escape_html } from "../../chunks/index3.js"; +import "clsx"; +import { p as page } from "../../chunks/index4.js"; +function _error($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + $$renderer2.push(`

${escape_html(page.status)}

`); + if (page.status === 404) { + $$renderer2.push(""); + $$renderer2.push(`Page not found`); + } else if (page.status === 403) { + $$renderer2.push(""); + $$renderer2.push(`Access denied`); + } else if (page.status === 401) { + $$renderer2.push(""); + $$renderer2.push(`Not logged in`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Something went wrong`); + } + $$renderer2.push(`

${escape_html(page.error?.message ?? "An unexpected error occurred.")}

Back to Dashboard
`); + }); +} +export { + _error as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/demo/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/demo/_page.svelte.js new file mode 100644 index 0000000..1b9e0e2 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/demo/_page.svelte.js @@ -0,0 +1,31 @@ +import { b as attr } from "../../../chunks/index3.js"; +import { i as initial_base, b as base } from "../../../chunks/server.js"; +import { r as resolve_route } from "../../../chunks/routing.js"; +import "../../../chunks/url.js"; +import { try_get_request_store } from "@sveltejs/kit/internal/server"; +import "../../../chunks/root.js"; +function resolve(id, params) { + const resolved = resolve_route( + id, + /** @type {Record} */ + params + ); + { + const store = try_get_request_store(); + if (store && !store.state.prerendering?.fallback) { + const after_base = store.event.url.pathname.slice(initial_base.length); + const segments = after_base.split("/").slice(2); + const prefix = segments.map(() => "..").join("/") || "."; + return prefix + resolved; + } + } + return base + resolved; +} +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + $$renderer2.push(`better-auth`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/demo/better-auth/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/_page.server.ts.js new file mode 100644 index 0000000..8fa1822 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/_page.server.ts.js @@ -0,0 +1,20 @@ +import { redirect } from "@sveltejs/kit"; +import { a as auth } from "../../../../chunks/auth.js"; +const load = async (event) => { + if (!event.locals.user) { + return redirect(302, "/demo/better-auth/login"); + } + return { user: event.locals.user }; +}; +const actions = { + signOut: async (event) => { + await auth.api.signOut({ + headers: event.request.headers + }); + return redirect(302, "/demo/better-auth/login"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/demo/better-auth/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/_page.svelte.js new file mode 100644 index 0000000..42126b3 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/_page.svelte.js @@ -0,0 +1,18 @@ +import { e as escape_html } from "../../../../chunks/index3.js"; +import "clsx"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + $$renderer2.push(`

Hi, ${escape_html(data.user.name)}!

Your user ID is ${escape_html(data.user.id)}.

`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/demo/better-auth/login/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/login/_page.server.ts.js new file mode 100644 index 0000000..2e2ee65 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/login/_page.server.ts.js @@ -0,0 +1,75 @@ +import { redirect, fail } from "@sveltejs/kit"; +import { a as auth } from "../../../../../chunks/auth.js"; +import "@better-auth/core/env"; +import "@better-auth/core/utils"; +import { APIError } from "better-call"; +import "@better-auth/core/api"; +const load = async (event) => { + if (event.locals.user) { + return redirect(302, "/demo/better-auth"); + } + return {}; +}; +const actions = { + signInEmail: async (event) => { + const formData = await event.request.formData(); + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + try { + await auth.api.signInEmail({ + body: { + email, + password, + callbackURL: "/auth/verification-success" + } + }); + } catch (error) { + if (error instanceof APIError) { + return fail(400, { message: error.message || "Signin failed" }); + } + return fail(500, { message: "Unexpected error" }); + } + return redirect(302, "/demo/better-auth"); + }, + signUpEmail: async (event) => { + const formData = await event.request.formData(); + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + const name = formData.get("name")?.toString() ?? ""; + try { + await auth.api.signUpEmail({ + body: { + email, + password, + name, + callbackURL: "/auth/verification-success" + } + }); + } catch (error) { + if (error instanceof APIError) { + return fail(400, { message: error.message || "Registration failed" }); + } + return fail(500, { message: "Unexpected error" }); + } + return redirect(302, "/demo/better-auth"); + }, + signInSocial: async (event) => { + const formData = await event.request.formData(); + const provider = formData.get("provider")?.toString() ?? "osu"; + const callbackURL = formData.get("callbackURL")?.toString() ?? "/demo/better-auth"; + const result = await auth.api.signInSocial({ + body: { + provider, + callbackURL + } + }); + if (result.url) { + return redirect(302, result.url); + } + return fail(400, { message: "Social sign-in failed" }); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/adapter-node/entries/pages/demo/better-auth/login/_page.svelte.js b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/login/_page.svelte.js new file mode 100644 index 0000000..c17dfef --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/demo/better-auth/login/_page.svelte.js @@ -0,0 +1,18 @@ +import { e as escape_html } from "../../../../../chunks/index3.js"; +import "clsx"; +import "@sveltejs/kit/internal"; +import "../../../../../chunks/url.js"; +import "../../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../../chunks/root.js"; +import "../../../../../chunks/exports.js"; +import "../../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { form } = $$props; + $$renderer2.push(`

Login

${escape_html(form?.message ?? "")}


`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/adapter-node/entries/pages/login/_page.server.ts.js b/.svelte-kit/adapter-node/entries/pages/login/_page.server.ts.js new file mode 100644 index 0000000..a8c7c75 --- /dev/null +++ b/.svelte-kit/adapter-node/entries/pages/login/_page.server.ts.js @@ -0,0 +1,16 @@ +import { redirect } from "@sveltejs/kit"; +import { a as auth } from "../../../chunks/auth.js"; +const load = async (event) => { + if (event.locals.user) redirect(302, "/"); + const result = await auth.api.signInSocial({ + body: { + provider: "osu", + callbackURL: "/" + } + }); + if (result.url) redirect(302, result.url); + return {}; +}; +export { + load +}; diff --git a/.svelte-kit/adapter-node/index.js b/.svelte-kit/adapter-node/index.js new file mode 100644 index 0000000..fb8a806 --- /dev/null +++ b/.svelte-kit/adapter-node/index.js @@ -0,0 +1,3698 @@ +import { D as DEV } from "./chunks/false.js"; +import { json, text, error } from "@sveltejs/kit"; +import { Redirect, SvelteKitError, ActionFailure, HttpError } from "@sveltejs/kit/internal"; +import { with_request_store, merge_tracing, try_get_request_store } from "@sveltejs/kit/internal/server"; +import { a as assets, b as base, c as app_dir, r as relative, o as override, d as reset } from "./chunks/server.js"; +import { E as ENDPOINT_METHODS, P as PAGE_METHODS, n as negotiate, m as method_not_allowed, h as handle_error_and_jsonify, g as get_status, i as is_form_content_type, a as normalize_error, b as get_global_name, s as serialize_uses, c as clarify_devalue_error, d as get_node_type, e as escape_html, S as SVELTE_KIT_ASSETS, f as create_remote_key, j as static_error_page, r as redirect_response, p as parse_remote_arg, k as stringify, l as deserialize_binary_form, o as has_prerendered_path, T as TRAILING_SLASH_PARAM, I as INVALIDATED_PARAM, q as handle_fatal_error, M as MUTATIVE_METHODS, t as format_server_error } from "./chunks/shared.js"; +import * as devalue from "devalue"; +import { m as make_trackable, d as disable_search, S as SCHEME, n as normalize_path, r as resolve, a as decode_pathname } from "./chunks/url.js"; +import { b as base64_encode, t as text_decoder, a as text_encoder, g as get_relative_path } from "./chunks/utils.js"; +import { w as writable, r as readable } from "./chunks/index.js"; +import { p as public_env, s as set_private_env, a as set_public_env } from "./chunks/shared-server.js"; +import { f as find_route } from "./chunks/routing.js"; +import { v as validate_layout_server_exports, a as validate_layout_exports, b as validate_page_server_exports, c as validate_page_exports, d as validate_server_exports } from "./chunks/exports.js"; +import { parse, serialize } from "cookie"; +import * as set_cookie_parser from "set-cookie-parser"; +import { r as read_implementation, o as options, g as get_hooks, s as set_read_implementation } from "./chunks/internal.js"; +function with_resolvers() { + let resolve2; + let reject; + const promise = new Promise((res, rej) => { + resolve2 = res; + reject = rej; + }); + return { promise, resolve: resolve2, reject }; +} +const NULL_BODY_STATUS = [101, 103, 204, 205, 304]; +const IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer; +async function render_endpoint(event, event_state, mod, state) { + const method = ( + /** @type {import('types').HttpMethod} */ + event.request.method + ); + let handler = mod[method] || mod.fallback; + if (method === "HEAD" && !mod.HEAD && mod.GET) { + handler = mod.GET; + } + if (!handler) { + return method_not_allowed(mod, method); + } + const prerender = mod.prerender ?? state.prerender_default; + if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) { + throw new Error("Cannot prerender endpoints that have mutative methods"); + } + if (state.prerendering && !state.prerendering.inside_reroute && !prerender) { + if (state.depth > 0) { + throw new Error(`${event.route.id} is not prerenderable`); + } else { + return new Response(void 0, { status: 204 }); + } + } + try { + event_state.allows_commands = true; + const response = await with_request_store( + { event, state: event_state }, + () => handler( + /** @type {import('@sveltejs/kit').RequestEvent>} */ + event + ) + ); + if (!(response instanceof Response)) { + throw new Error( + `Invalid response from route ${event.url.pathname}: handler should return a Response object` + ); + } + if (state.prerendering && (!state.prerendering.inside_reroute || prerender)) { + const cloned = new Response(response.clone().body, { + status: response.status, + statusText: response.statusText, + headers: new Headers(response.headers) + }); + cloned.headers.set("x-sveltekit-prerender", String(prerender)); + if (state.prerendering.inside_reroute && prerender) { + cloned.headers.set( + "x-sveltekit-routeid", + encodeURI( + /** @type {string} */ + event.route.id + ) + ); + state.prerendering.dependencies.set(event.url.pathname, { response: cloned, body: null }); + } else { + return cloned; + } + } + return response; + } catch (e) { + if (e instanceof Redirect) { + return new Response(void 0, { + status: e.status, + headers: { location: e.location } + }); + } + throw e; + } +} +function is_endpoint_request(event) { + const { method, headers: headers2 } = event.request; + if (ENDPOINT_METHODS.includes(method) && !PAGE_METHODS.includes(method)) { + return true; + } + if (method === "POST" && headers2.get("x-sveltekit-action") === "true") return false; + const accept = event.request.headers.get("accept") ?? "*/*"; + return negotiate(accept, ["*", "text/html"]) !== "text/html"; +} +function compact(arr) { + return arr.filter( + /** @returns {val is NonNullable} */ + (val) => val != null + ); +} +const DATA_SUFFIX = "/__data.json"; +const HTML_DATA_SUFFIX = ".html__data.json"; +function has_data_suffix(pathname) { + return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX); +} +function add_data_suffix(pathname) { + if (pathname.endsWith(".html")) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX); + return pathname.replace(/\/$/, "") + DATA_SUFFIX; +} +function strip_data_suffix(pathname) { + if (pathname.endsWith(HTML_DATA_SUFFIX)) { + return pathname.slice(0, -HTML_DATA_SUFFIX.length) + ".html"; + } + return pathname.slice(0, -DATA_SUFFIX.length); +} +const ROUTE_SUFFIX = "/__route.js"; +function has_resolution_suffix(pathname) { + return pathname.endsWith(ROUTE_SUFFIX); +} +function add_resolution_suffix(pathname) { + return pathname.replace(/\/$/, "") + ROUTE_SUFFIX; +} +function strip_resolution_suffix(pathname) { + return pathname.slice(0, -ROUTE_SUFFIX.length); +} +const noop_span = { + spanContext() { + return noop_span_context; + }, + setAttribute() { + return this; + }, + setAttributes() { + return this; + }, + addEvent() { + return this; + }, + setStatus() { + return this; + }, + updateName() { + return this; + }, + end() { + return this; + }, + isRecording() { + return false; + }, + recordException() { + return this; + }, + addLink() { + return this; + }, + addLinks() { + return this; + } +}; +const noop_span_context = { + traceId: "", + spanId: "", + traceFlags: 0 +}; +async function record_span({ name, attributes, fn }) { + { + return fn(noop_span); + } +} +function is_action_json_request(event) { + const accept = negotiate(event.request.headers.get("accept") ?? "*/*", [ + "application/json", + "text/html" + ]); + return accept === "application/json" && event.request.method === "POST"; +} +async function handle_action_json_request(event, event_state, options2, server) { + const actions = server?.actions; + if (!actions) { + const no_actions_error = new SvelteKitError( + 405, + "Method Not Allowed", + `POST method not allowed. No form actions exist for ${"this page"}` + ); + return action_json( + { + type: "error", + error: await handle_error_and_jsonify(event, event_state, options2, no_actions_error) + }, + { + status: no_actions_error.status, + headers: { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: "GET" + } + } + ); + } + check_named_default_separate(actions); + try { + const data = await call_action(event, event_state, actions); + if (DEV) ; + if (data instanceof ActionFailure) { + return action_json({ + type: "failure", + status: data.status, + // @ts-expect-error we assign a string to what is supposed to be an object. That's ok + // because we don't use the object outside, and this way we have better code navigation + // through knowing where the related interface is used. + data: stringify_action_response( + data.data, + /** @type {string} */ + event.route.id, + options2.hooks.transport + ) + }); + } else { + return action_json({ + type: "success", + status: data ? 200 : 204, + // @ts-expect-error see comment above + data: stringify_action_response( + data, + /** @type {string} */ + event.route.id, + options2.hooks.transport + ) + }); + } + } catch (e) { + const err = normalize_error(e); + if (err instanceof Redirect) { + return action_json_redirect(err); + } + return action_json( + { + type: "error", + error: await handle_error_and_jsonify( + event, + event_state, + options2, + check_incorrect_fail_use(err) + ) + }, + { + status: get_status(err) + } + ); + } +} +function check_incorrect_fail_use(error2) { + return error2 instanceof ActionFailure ? new Error('Cannot "throw fail()". Use "return fail()"') : error2; +} +function action_json_redirect(redirect) { + return action_json({ + type: "redirect", + status: redirect.status, + location: redirect.location + }); +} +function action_json(data, init2) { + return json(data, init2); +} +function is_action_request(event) { + return event.request.method === "POST"; +} +async function handle_action_request(event, event_state, server) { + const actions = server?.actions; + if (!actions) { + event.setHeaders({ + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: "GET" + }); + return { + type: "error", + error: new SvelteKitError( + 405, + "Method Not Allowed", + `POST method not allowed. No form actions exist for ${"this page"}` + ) + }; + } + check_named_default_separate(actions); + try { + const data = await call_action(event, event_state, actions); + if (DEV) ; + if (data instanceof ActionFailure) { + return { + type: "failure", + status: data.status, + data: data.data + }; + } else { + return { + type: "success", + status: 200, + // @ts-expect-error this will be removed upon serialization, so `undefined` is the same as omission + data + }; + } + } catch (e) { + const err = normalize_error(e); + if (err instanceof Redirect) { + return { + type: "redirect", + status: err.status, + location: err.location + }; + } + return { + type: "error", + error: check_incorrect_fail_use(err) + }; + } +} +function check_named_default_separate(actions) { + if (actions.default && Object.keys(actions).length > 1) { + throw new Error( + "When using named actions, the default action cannot be used. See the docs for more info: https://svelte.dev/docs/kit/form-actions#named-actions" + ); + } +} +async function call_action(event, event_state, actions) { + const url = new URL(event.request.url); + let name = "default"; + for (const param of url.searchParams) { + if (param[0].startsWith("/")) { + name = param[0].slice(1); + if (name === "default") { + throw new Error('Cannot use reserved action name "default"'); + } + break; + } + } + const action = actions[name]; + if (!action) { + throw new SvelteKitError(404, "Not Found", `No action with name '${name}' found`); + } + if (!is_form_content_type(event.request)) { + throw new SvelteKitError( + 415, + "Unsupported Media Type", + `Form actions expect form-encoded data — received ${event.request.headers.get( + "content-type" + )}` + ); + } + return record_span({ + name: "sveltekit.form_action", + attributes: { + "http.route": event.route.id || "unknown" + }, + fn: async (current2) => { + const traced_event = merge_tracing(event, current2); + event_state.allows_commands = true; + const result = await with_request_store( + { event: traced_event, state: event_state }, + () => action(traced_event) + ); + if (result instanceof ActionFailure) { + current2.setAttributes({ + "sveltekit.form_action.result.type": "failure", + "sveltekit.form_action.result.status": result.status + }); + } + return result; + } + }); +} +function validate_action_return(data) { + if (data instanceof Redirect) { + throw new Error("Cannot `return redirect(...)` — use `redirect(...)` instead"); + } + if (data instanceof HttpError) { + throw new Error("Cannot `return error(...)` — use `error(...)` or `return fail(...)` instead"); + } +} +function uneval_action_response(data, route_id, transport) { + const replacer = (thing) => { + for (const key2 in transport) { + const encoded = transport[key2].encode(thing); + if (encoded) { + return `app.decode('${key2}', ${devalue.uneval(encoded, replacer)})`; + } + } + }; + return try_serialize(data, (value) => devalue.uneval(value, replacer), route_id); +} +function stringify_action_response(data, route_id, transport) { + const encoders = Object.fromEntries( + Object.entries(transport).map(([key2, value]) => [key2, value.encode]) + ); + return try_serialize(data, (value) => devalue.stringify(value, encoders), route_id); +} +function try_serialize(data, fn, route_id) { + try { + return fn(data); + } catch (e) { + const error2 = ( + /** @type {any} */ + e + ); + if (data instanceof Response) { + throw new Error( + `Data returned from action inside ${route_id} is not serializable. Form actions need to return plain objects or fail(). E.g. return { success: true } or return fail(400, { message: "invalid" });`, + { cause: e } + ); + } + if ("path" in error2) { + let message = `Data returned from action inside ${route_id} is not serializable: ${error2.message}`; + if (error2.path !== "") message += ` (data.${error2.path})`; + throw new Error(message, { cause: e }); + } + throw error2; + } +} +function create_async_iterator() { + let resolved = -1; + let returned = -1; + const deferred = []; + return { + iterate: (transform = (x) => x) => { + return { + [Symbol.asyncIterator]() { + return { + next: async () => { + const next = deferred[++returned]; + if (!next) return { value: null, done: true }; + const value = await next.promise; + return { value: transform(value), done: false }; + } + }; + } + }; + }, + add: (promise) => { + deferred.push(with_resolvers()); + void promise.then((value) => { + deferred[++resolved].resolve(value); + }); + } + }; +} +function server_data_serializer(event, event_state, options2) { + let promise_id = 1; + let max_nodes = -1; + const iterator = create_async_iterator(); + const global = get_global_name(options2); + function get_replacer(index) { + return function replacer(thing) { + if (typeof thing?.then === "function") { + const id = promise_id++; + const promise = thing.then( + /** @param {any} data */ + (data) => ({ data }) + ).catch( + /** @param {any} error */ + async (error2) => ({ + error: await handle_error_and_jsonify(event, event_state, options2, error2) + }) + ).then( + /** + * @param {{data: any; error: any}} result + */ + async ({ data, error: error2 }) => { + let str; + try { + str = devalue.uneval(error2 ? [, error2] : [data], replacer); + } catch { + error2 = await handle_error_and_jsonify( + event, + event_state, + options2, + new Error(`Failed to serialize promise while rendering ${event.route.id}`) + ); + str = devalue.uneval([, error2], replacer); + } + return { + index, + str: `${global}.resolve(${id}, ${str.includes("app.decode") ? `(app) => ${str}` : `() => ${str}`})` + }; + } + ); + iterator.add(promise); + return `${global}.defer(${id})`; + } else { + for (const key2 in options2.hooks.transport) { + const encoded = options2.hooks.transport[key2].encode(thing); + if (encoded) { + return `app.decode('${key2}', ${devalue.uneval(encoded, replacer)})`; + } + } + } + }; + } + const strings = ( + /** @type {string[]} */ + [] + ); + return { + set_max_nodes(i) { + max_nodes = i; + }, + add_node(i, node) { + try { + if (!node) { + strings[i] = "null"; + return; + } + const payload = { type: "data", data: node.data, uses: serialize_uses(node) }; + if (node.slash) payload.slash = node.slash; + strings[i] = devalue.uneval(payload, get_replacer(i)); + } catch (e) { + e.path = e.path.slice(1); + throw new Error(clarify_devalue_error( + event, + /** @type {any} */ + e + ), { cause: e }); + } + }, + get_data(csp) { + const open = ``; + const close = `<\/script> +`; + return { + data: `[${compact(max_nodes > -1 ? strings.slice(0, max_nodes) : strings).join(",")}]`, + chunks: promise_id > 1 ? iterator.iterate(({ index, str }) => { + if (max_nodes > -1 && index >= max_nodes) { + return ""; + } + return open + str + close; + }) : null + }; + } + }; +} +function server_data_serializer_json(event, event_state, options2) { + let promise_id = 1; + const iterator = create_async_iterator(); + const reducers = { + ...Object.fromEntries( + Object.entries(options2.hooks.transport).map(([key2, value]) => [key2, value.encode]) + ), + /** @param {any} thing */ + Promise: (thing) => { + if (typeof thing?.then !== "function") { + return; + } + const id = promise_id++; + let key2 = "data"; + const promise = thing.catch( + /** @param {any} e */ + async (e) => { + key2 = "error"; + return handle_error_and_jsonify( + event, + event_state, + options2, + /** @type {any} */ + e + ); + } + ).then( + /** @param {any} value */ + async (value) => { + let str; + try { + str = devalue.stringify(value, reducers); + } catch { + const error2 = await handle_error_and_jsonify( + event, + event_state, + options2, + new Error(`Failed to serialize promise while rendering ${event.route.id}`) + ); + key2 = "error"; + str = devalue.stringify(error2, reducers); + } + return `{"type":"chunk","id":${id},"${key2}":${str}} +`; + } + ); + iterator.add(promise); + return id; + } + }; + const strings = ( + /** @type {string[]} */ + [] + ); + return { + add_node(i, node) { + try { + if (!node) { + strings[i] = "null"; + return; + } + if (node.type === "error" || node.type === "skip") { + strings[i] = JSON.stringify(node); + return; + } + strings[i] = `{"type":"data","data":${devalue.stringify(node.data, reducers)},"uses":${JSON.stringify( + serialize_uses(node) + )}${node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ""}}`; + } catch (e) { + e.path = "data" + e.path; + throw new Error(clarify_devalue_error( + event, + /** @type {any} */ + e + ), { cause: e }); + } + }, + get_data() { + return { + data: `{"type":"data","nodes":[${strings.join(",")}]} +`, + chunks: promise_id > 1 ? iterator.iterate() : null + }; + } + }; +} +async function load_server_data({ event, event_state, state, node, parent }) { + if (!node?.server) return null; + let is_tracking = true; + const uses = { + dependencies: /* @__PURE__ */ new Set(), + params: /* @__PURE__ */ new Set(), + parent: false, + route: false, + url: false, + search_params: /* @__PURE__ */ new Set() + }; + const load = node.server.load; + const slash = node.server.trailingSlash; + if (!load) { + return { type: "data", data: null, uses, slash }; + } + const url = make_trackable( + event.url, + () => { + if (is_tracking) { + uses.url = true; + } + }, + (param) => { + if (is_tracking) { + uses.search_params.add(param); + } + } + ); + if (state.prerendering) { + disable_search(url); + } + const result = await record_span({ + name: "sveltekit.load", + attributes: { + "sveltekit.load.node_id": node.server_id || "unknown", + "sveltekit.load.node_type": get_node_type(node.server_id), + "http.route": event.route.id || "unknown" + }, + fn: async (current2) => { + const traced_event = merge_tracing(event, current2); + const result2 = await with_request_store( + { event: traced_event, state: event_state }, + () => load.call(null, { + ...traced_event, + fetch: (info, init2) => { + new URL(info instanceof Request ? info.url : info, event.url); + return event.fetch(info, init2); + }, + /** @param {string[]} deps */ + depends: (...deps) => { + for (const dep of deps) { + const { href } = new URL(dep, event.url); + uses.dependencies.add(href); + } + }, + params: new Proxy(event.params, { + get: (target, key2) => { + if (is_tracking) { + uses.params.add(key2); + } + return target[ + /** @type {string} */ + key2 + ]; + } + }), + parent: async () => { + if (is_tracking) { + uses.parent = true; + } + return parent(); + }, + route: new Proxy(event.route, { + get: (target, key2) => { + if (is_tracking) { + uses.route = true; + } + return target[ + /** @type {'id'} */ + key2 + ]; + } + }), + url, + untrack(fn) { + is_tracking = false; + try { + return fn(); + } finally { + is_tracking = true; + } + } + }) + ); + return result2; + } + }); + return { + type: "data", + data: result ?? null, + uses, + slash + }; +} +async function load_data({ + event, + event_state, + fetched, + node, + parent, + server_data_promise, + state, + resolve_opts, + csr +}) { + const server_data_node = await server_data_promise; + const load = node?.universal?.load; + if (!load) { + return server_data_node?.data ?? null; + } + const result = await record_span({ + name: "sveltekit.load", + attributes: { + "sveltekit.load.node_id": node.universal_id || "unknown", + "sveltekit.load.node_type": get_node_type(node.universal_id), + "http.route": event.route.id || "unknown" + }, + fn: async (current2) => { + const traced_event = merge_tracing(event, current2); + return await with_request_store( + { event: traced_event, state: event_state }, + () => load.call(null, { + url: event.url, + params: event.params, + data: server_data_node?.data ?? null, + route: event.route, + fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts), + setHeaders: event.setHeaders, + depends: () => { + }, + parent, + untrack: (fn) => fn(), + tracing: traced_event.tracing + }) + ); + } + }); + return result ?? null; +} +function create_universal_fetch(event, state, fetched, csr, resolve_opts) { + const universal_fetch = async (input, init2) => { + const cloned_body = input instanceof Request && input.body ? input.clone().body : null; + const cloned_headers = input instanceof Request && [...input.headers].length ? new Headers(input.headers) : init2?.headers; + let response = await event.fetch(input, init2); + const url = new URL(input instanceof Request ? input.url : input, event.url); + const same_origin = url.origin === event.url.origin; + let dependency; + if (same_origin) { + if (state.prerendering) { + dependency = { response, body: null }; + state.prerendering.dependencies.set(url.pathname, dependency); + } + } else if (url.protocol === "https:" || url.protocol === "http:") { + const mode = input instanceof Request ? input.mode : init2?.mode ?? "cors"; + if (mode === "no-cors") { + response = new Response("", { + status: response.status, + statusText: response.statusText, + headers: response.headers + }); + } else { + const acao = response.headers.get("access-control-allow-origin"); + if (!acao || acao !== event.url.origin && acao !== "*") { + throw new Error( + `CORS error: ${acao ? "Incorrect" : "No"} 'Access-Control-Allow-Origin' header is present on the requested resource` + ); + } + } + } + let teed_body; + const proxy = new Proxy(response, { + get(response2, key2, receiver) { + async function push_fetched(body2, is_b64) { + const status_number = Number(response2.status); + if (isNaN(status_number)) { + throw new Error( + `response.status is not a number. value: "${response2.status}" type: ${typeof response2.status}` + ); + } + fetched.push({ + url: same_origin ? url.href.slice(event.url.origin.length) : url.href, + method: event.request.method, + request_body: ( + /** @type {string | ArrayBufferView | undefined} */ + input instanceof Request && cloned_body ? await stream_to_string(cloned_body) : init2?.body + ), + request_headers: cloned_headers, + response_body: body2, + response: response2, + is_b64 + }); + } + if (key2 === "body") { + if (response2.body === null) { + return null; + } + if (teed_body) { + return teed_body; + } + const [a, b] = response2.body.tee(); + void (async () => { + let result = new Uint8Array(); + for await (const chunk of a) { + const combined = new Uint8Array(result.length + chunk.length); + combined.set(result, 0); + combined.set(chunk, result.length); + result = combined; + } + if (dependency) { + dependency.body = new Uint8Array(result); + } + void push_fetched(base64_encode(result), true); + })(); + return teed_body = b; + } + if (key2 === "arrayBuffer") { + return async () => { + const buffer = await response2.arrayBuffer(); + const bytes = new Uint8Array(buffer); + if (dependency) { + dependency.body = bytes; + } + if (buffer instanceof ArrayBuffer) { + await push_fetched(base64_encode(bytes), true); + } + return buffer; + }; + } + async function text2() { + const body2 = await response2.text(); + if (body2 === "" && NULL_BODY_STATUS.includes(response2.status)) { + await push_fetched(void 0, false); + return void 0; + } + if (!body2 || typeof body2 === "string") { + await push_fetched(body2, false); + } + if (dependency) { + dependency.body = body2; + } + return body2; + } + if (key2 === "text") { + return text2; + } + if (key2 === "json") { + return async () => { + const body2 = await text2(); + return body2 ? JSON.parse(body2) : void 0; + }; + } + const value = Reflect.get(response2, key2, response2); + if (value instanceof Function) { + return Object.defineProperties( + /** + * @this {any} + */ + function() { + return Reflect.apply(value, this === receiver ? response2 : this, arguments); + }, + { + name: { value: value.name }, + length: { value: value.length } + } + ); + } + return value; + } + }); + if (csr) { + const get = response.headers.get; + response.headers.get = (key2) => { + const lower = key2.toLowerCase(); + const value = get.call(response.headers, lower); + if (value && !lower.startsWith("x-sveltekit-")) { + const included = resolve_opts.filterSerializedResponseHeaders(lower, value); + if (!included) { + throw new Error( + `Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://svelte.dev/docs/kit/hooks#Server-hooks-handle (at ${event.route.id})` + ); + } + } + return value; + }; + } + return proxy; + }; + return (input, init2) => { + const response = universal_fetch(input, init2); + response.catch(() => { + }); + return response; + }; +} +async function stream_to_string(stream) { + let result = ""; + const reader = stream.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) { + break; + } + result += text_decoder.decode(value); + } + return result; +} +function hash(...values) { + let hash2 = 5381; + for (const value of values) { + if (typeof value === "string") { + let i = value.length; + while (i) hash2 = hash2 * 33 ^ value.charCodeAt(--i); + } else if (ArrayBuffer.isView(value)) { + const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength); + let i = buffer.length; + while (i) hash2 = hash2 * 33 ^ buffer[--i]; + } else { + throw new TypeError("value must be a string or TypedArray"); + } + } + return (hash2 >>> 0).toString(36); +} +const replacements = { + "<": "\\u003C", + "\u2028": "\\u2028", + "\u2029": "\\u2029" +}; +const pattern = new RegExp(`[${Object.keys(replacements).join("")}]`, "g"); +function serialize_data(fetched, filter, prerendering = false) { + const headers2 = {}; + let cache_control = null; + let age = null; + let varyAny = false; + for (const [key2, value] of fetched.response.headers) { + if (filter(key2, value)) { + headers2[key2] = value; + } + if (key2 === "cache-control") cache_control = value; + else if (key2 === "age") age = value; + else if (key2 === "vary" && value.trim() === "*") varyAny = true; + } + const payload = { + status: fetched.response.status, + statusText: fetched.response.statusText, + headers: headers2, + body: fetched.response_body + }; + const safe_payload = JSON.stringify(payload).replace(pattern, (match) => replacements[match]); + const attrs = [ + 'type="application/json"', + "data-sveltekit-fetched", + `data-url="${escape_html(fetched.url, true)}"` + ]; + if (fetched.is_b64) { + attrs.push("data-b64"); + } + if (fetched.request_headers || fetched.request_body) { + const values = []; + if (fetched.request_headers) { + values.push([...new Headers(fetched.request_headers)].join(",")); + } + if (fetched.request_body) { + values.push(fetched.request_body); + } + attrs.push(`data-hash="${hash(...values)}"`); + } + if (!prerendering && fetched.method === "GET" && cache_control && !varyAny) { + const match = /s-maxage=(\d+)/g.exec(cache_control) ?? /max-age=(\d+)/g.exec(cache_control); + if (match) { + const ttl = +match[1] - +(age ?? "0"); + attrs.push(`data-ttl="${ttl}"`); + } + } + return ` + +{#if constructors[1]} + {@const Pyramid_0 = constructors[0]} + + + {#if constructors[2]} + {@const Pyramid_1 = constructors[1]} + + + + + + + {:else} + {@const Pyramid_1 = constructors[1]} + + + + {/if} + + +{:else} + {@const Pyramid_0 = constructors[0]} + + + +{/if} + +{#if mounted} +
+ {#if navigated} + {title} + {/if} +
+{/if} \ No newline at end of file diff --git a/.svelte-kit/generated/server/internal.js b/.svelte-kit/generated/server/internal.js new file mode 100644 index 0000000..db0dd6a --- /dev/null +++ b/.svelte-kit/generated/server/internal.js @@ -0,0 +1,53 @@ + +import root from '../root.js'; +import { set_building, set_prerendering } from '__sveltekit/environment'; +import { set_assets } from '$app/paths/internal/server'; +import { set_manifest, set_read_implementation } from '__sveltekit/server'; +import { set_private_env, set_public_env } from '../../../node_modules/@sveltejs/kit/src/runtime/shared-server.js'; + +export const options = { + app_template_contains_nonce: false, + async: false, + csp: {"mode":"auto","directives":{"upgrade-insecure-requests":false,"block-all-mixed-content":false},"reportOnly":{"upgrade-insecure-requests":false,"block-all-mixed-content":false}}, + csrf_check_origin: true, + csrf_trusted_origins: [], + embedded: false, + env_public_prefix: 'PUBLIC_', + env_private_prefix: '', + hash_routing: false, + hooks: null, // added lazily, via `get_hooks` + preload_strategy: "modulepreload", + root, + service_worker: false, + service_worker_options: undefined, + templates: { + app: ({ head, body, assets, nonce, env }) => "\n\n\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\tVash Esports\n\t\t" + head + "\n\t\n\t\n\t\t
" + body + "
\n\t\n\n", + error: ({ status, message }) => "\n\n\t\n\t\t\n\t\t" + message + "\n\n\t\t\n\t\n\t\n\t\t
\n\t\t\t" + status + "\n\t\t\t
\n\t\t\t\t

" + message + "

\n\t\t\t
\n\t\t
\n\t\n\n" + }, + version_hash: "12t8tv8" +}; + +export async function get_hooks() { + let handle; + let handleFetch; + let handleError; + let handleValidationError; + let init; + ({ handle, handleFetch, handleError, handleValidationError, init } = await import("../../../src/hooks.server.ts")); + + let reroute; + let transport; + + + return { + handle, + handleFetch, + handleError, + handleValidationError, + init, + reroute, + transport + }; +} + +export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation }; diff --git a/.svelte-kit/non-ambient.d.ts b/.svelte-kit/non-ambient.d.ts new file mode 100644 index 0000000..c14fd87 --- /dev/null +++ b/.svelte-kit/non-ambient.d.ts @@ -0,0 +1,81 @@ + +// this file is generated — do not edit it + + +declare module "svelte/elements" { + export interface HTMLAttributes { + 'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null; + 'data-sveltekit-noscroll'?: true | '' | 'off' | undefined | null; + 'data-sveltekit-preload-code'?: + | true + | '' + | 'eager' + | 'viewport' + | 'hover' + | 'tap' + | 'off' + | undefined + | null; + 'data-sveltekit-preload-data'?: true | '' | 'hover' | 'tap' | 'off' | undefined | null; + 'data-sveltekit-reload'?: true | '' | 'off' | undefined | null; + 'data-sveltekit-replacestate'?: true | '' | 'off' | undefined | null; + } +} + +export {}; + + +declare module "$app/types" { + export interface AppTypes { + RouteId(): "/(app)" | "/" | "/(app)/admin" | "/api" | "/api/health" | "/api/mappools" | "/api/mappools/[id]" | "/api/mappools/[id]/slots" | "/api/mappools/[id]/slots/[slotId]" | "/api/matches" | "/api/matches/[id]" | "/api/matches/[id]/lobby" | "/api/matches/[id]/pick" | "/api/matches/[id]/rolling" | "/api/matches/[id]/roll" | "/api/matches/[id]/score" | "/api/osu" | "/api/osu/beatmap" | "/api/osu/beatmap/[id]" | "/api/queue" | "/demo" | "/demo/better-auth" | "/demo/better-auth/login" | "/login" | "/(app)/mappools" | "/(app)/mappools/[id]" | "/(app)/matches" | "/(app)/matches/[id]" | "/(app)/settings" | "/(app)/teams"; + RouteParams(): { + "/api/mappools/[id]": { id: string }; + "/api/mappools/[id]/slots": { id: string }; + "/api/mappools/[id]/slots/[slotId]": { id: string; slotId: string }; + "/api/matches/[id]": { id: string }; + "/api/matches/[id]/lobby": { id: string }; + "/api/matches/[id]/pick": { id: string }; + "/api/matches/[id]/rolling": { id: string }; + "/api/matches/[id]/roll": { id: string }; + "/api/matches/[id]/score": { id: string }; + "/api/osu/beatmap/[id]": { id: string }; + "/(app)/mappools/[id]": { id: string }; + "/(app)/matches/[id]": { id: string } + }; + LayoutParams(): { + "/(app)": { id?: string }; + "/": { id?: string; slotId?: string }; + "/(app)/admin": Record; + "/api": { id?: string; slotId?: string }; + "/api/health": Record; + "/api/mappools": { id?: string; slotId?: string }; + "/api/mappools/[id]": { id: string; slotId?: string }; + "/api/mappools/[id]/slots": { id: string; slotId?: string }; + "/api/mappools/[id]/slots/[slotId]": { id: string; slotId: string }; + "/api/matches": { id?: string }; + "/api/matches/[id]": { id: string }; + "/api/matches/[id]/lobby": { id: string }; + "/api/matches/[id]/pick": { id: string }; + "/api/matches/[id]/rolling": { id: string }; + "/api/matches/[id]/roll": { id: string }; + "/api/matches/[id]/score": { id: string }; + "/api/osu": { id?: string }; + "/api/osu/beatmap": { id?: string }; + "/api/osu/beatmap/[id]": { id: string }; + "/api/queue": Record; + "/demo": Record; + "/demo/better-auth": Record; + "/demo/better-auth/login": Record; + "/login": Record; + "/(app)/mappools": { id?: string }; + "/(app)/mappools/[id]": { id: string }; + "/(app)/matches": { id?: string }; + "/(app)/matches/[id]": { id: string }; + "/(app)/settings": Record; + "/(app)/teams": Record + }; + Pathname(): "/" | "/admin" | "/api/health" | "/api/mappools" | `/api/mappools/${string}` & {} | `/api/mappools/${string}/slots` & {} | `/api/mappools/${string}/slots/${string}` & {} | "/api/matches" | `/api/matches/${string}` & {} | `/api/matches/${string}/lobby` & {} | `/api/matches/${string}/pick` & {} | `/api/matches/${string}/rolling` & {} | `/api/matches/${string}/roll` & {} | `/api/matches/${string}/score` & {} | `/api/osu/beatmap/${string}` & {} | "/api/queue" | "/demo" | "/demo/better-auth" | "/demo/better-auth/login" | "/login" | "/mappools" | `/mappools/${string}` & {} | "/matches" | `/matches/${string}` & {} | "/settings" | "/teams"; + ResolvedPathname(): `${"" | `/${string}`}${ReturnType}`; + Asset(): "/android-chrome-192x192.png" | "/android-chrome-512x512.png" | "/apple-touch-icon.png" | "/favicon-16x16.png" | "/favicon-32x32.png" | "/favicon.ico" | "/robots.txt" | "/site.webmanifest" | string & {}; + } +} \ No newline at end of file diff --git a/.svelte-kit/output/client/.vite/manifest.json b/.svelte-kit/output/client/.vite/manifest.json new file mode 100644 index 0000000..f1cdb44 --- /dev/null +++ b/.svelte-kit/output/client/.vite/manifest.json @@ -0,0 +1,394 @@ +{ + ".svelte-kit/generated/client-optimized/app.js": { + "file": "_app/immutable/entry/app.DL20j9gb.js", + "name": "entry/app", + "src": ".svelte-kit/generated/client-optimized/app.js", + "isEntry": true, + "imports": [ + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_DsnmJJEf.js", + "_CvGSsBh3.js", + "_BQKoxf2Q.js", + "_kALFGdUG.js" + ], + "dynamicImports": [ + ".svelte-kit/generated/client-optimized/nodes/0.js", + ".svelte-kit/generated/client-optimized/nodes/1.js", + ".svelte-kit/generated/client-optimized/nodes/2.js", + ".svelte-kit/generated/client-optimized/nodes/3.js", + ".svelte-kit/generated/client-optimized/nodes/4.js", + ".svelte-kit/generated/client-optimized/nodes/5.js", + ".svelte-kit/generated/client-optimized/nodes/6.js", + ".svelte-kit/generated/client-optimized/nodes/7.js", + ".svelte-kit/generated/client-optimized/nodes/8.js", + ".svelte-kit/generated/client-optimized/nodes/9.js", + ".svelte-kit/generated/client-optimized/nodes/10.js", + ".svelte-kit/generated/client-optimized/nodes/11.js", + ".svelte-kit/generated/client-optimized/nodes/12.js", + ".svelte-kit/generated/client-optimized/nodes/13.js", + "_BQKoxf2Q.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/0.js": { + "file": "_app/immutable/nodes/0.9C686m9W.js", + "name": "nodes/0", + "src": ".svelte-kit/generated/client-optimized/nodes/0.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_CyUoLS3q.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/1.js": { + "file": "_app/immutable/nodes/1.RyK1bdTz.js", + "name": "nodes/1", + "src": ".svelte-kit/generated/client-optimized/nodes/1.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_TpZZa089.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_D2SGgx9j.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/10.js": { + "file": "_app/immutable/nodes/10.BPlEN6_G.js", + "name": "nodes/10", + "src": ".svelte-kit/generated/client-optimized/nodes/10.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BLQQas77.js", + "_BAocuFgQ.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js", + "_CAkKRSSs.js", + "_DAtZQVjv.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/11.js": { + "file": "_app/immutable/nodes/11.DR1Vz7m0.js", + "name": "nodes/11", + "src": ".svelte-kit/generated/client-optimized/nodes/11.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_TpZZa089.js", + "_BxEwC6Zc.js", + "_BnX5ZnVA.js", + "_DAtZQVjv.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/12.js": { + "file": "_app/immutable/nodes/12.C9UZWAjJ.js", + "name": "nodes/12", + "src": ".svelte-kit/generated/client-optimized/nodes/12.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BAocuFgQ.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/13.js": { + "file": "_app/immutable/nodes/13.CyZ4e6yK.js", + "name": "nodes/13", + "src": ".svelte-kit/generated/client-optimized/nodes/13.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BAocuFgQ.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/2.js": { + "file": "_app/immutable/nodes/2.BsR2_q_w.js", + "name": "nodes/2", + "src": ".svelte-kit/generated/client-optimized/nodes/2.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_CyUoLS3q.js", + "_BQKoxf2Q.js", + "_DoLu9QLy.js", + "_BLQQas77.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js", + "_D2SGgx9j.js", + "_BAocuFgQ.js" + ], + "css": [ + "_app/immutable/assets/2.Dv2no-8m.css" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/3.js": { + "file": "_app/immutable/nodes/3.CMuy9Bx3.js", + "name": "nodes/3", + "src": ".svelte-kit/generated/client-optimized/nodes/3.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BLQQas77.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/4.js": { + "file": "_app/immutable/nodes/4.BdC-uDW-.js", + "name": "nodes/4", + "src": ".svelte-kit/generated/client-optimized/nodes/4.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BLQQas77.js", + "_BAocuFgQ.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js", + "_Cy2QBcXt.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/5.js": { + "file": "_app/immutable/nodes/5.CyqZcxti.js", + "name": "nodes/5", + "src": ".svelte-kit/generated/client-optimized/nodes/5.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BLQQas77.js", + "_BAocuFgQ.js", + "_BnX5ZnVA.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/6.js": { + "file": "_app/immutable/nodes/6.CEe0czaO.js", + "name": "nodes/6", + "src": ".svelte-kit/generated/client-optimized/nodes/6.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BLQQas77.js", + "_BAocuFgQ.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js", + "_CAkKRSSs.js", + "_Cy2QBcXt.js", + "_DAtZQVjv.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/7.js": { + "file": "_app/immutable/nodes/7._nuD3LlN.js", + "name": "nodes/7", + "src": ".svelte-kit/generated/client-optimized/nodes/7.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BLQQas77.js", + "_BAocuFgQ.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/8.js": { + "file": "_app/immutable/nodes/8.CkbqikZq.js", + "name": "nodes/8", + "src": ".svelte-kit/generated/client-optimized/nodes/8.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BLQQas77.js", + "_BAocuFgQ.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js", + "_BkIq08el.js" + ] + }, + ".svelte-kit/generated/client-optimized/nodes/9.js": { + "file": "_app/immutable/nodes/9.BTBc_Eli.js", + "name": "nodes/9", + "src": ".svelte-kit/generated/client-optimized/nodes/9.js", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_DsnmJJEf.js", + "_BxEwC6Zc.js", + "_DoLu9QLy.js", + "_BQKoxf2Q.js", + "_BAocuFgQ.js", + "_BnX5ZnVA.js", + "_BqOsL1Vv.js" + ] + }, + "_BAocuFgQ.js": { + "file": "_app/immutable/chunks/BAocuFgQ.js", + "name": "forms", + "imports": [ + "_BxEwC6Zc.js", + "_BkIq08el.js" + ] + }, + "_BLQQas77.js": { + "file": "_app/immutable/chunks/BLQQas77.js", + "name": "each", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "_BQKoxf2Q.js": { + "file": "_app/immutable/chunks/BQKoxf2Q.js", + "name": "14", + "isDynamicEntry": true, + "imports": [ + "_BxEwC6Zc.js", + "_kALFGdUG.js" + ] + }, + "_BkIq08el.js": { + "file": "_app/immutable/chunks/BkIq08el.js", + "name": "entry", + "imports": [ + "_CvGSsBh3.js", + "_DAtZQVjv.js", + "_BxEwC6Zc.js" + ] + }, + "_BnX5ZnVA.js": { + "file": "_app/immutable/chunks/BnX5ZnVA.js", + "name": "attributes", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "_BqOsL1Vv.js": { + "file": "_app/immutable/chunks/BqOsL1Vv.js", + "name": "class", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "_BxEwC6Zc.js": { + "file": "_app/immutable/chunks/BxEwC6Zc.js", + "name": "template" + }, + "_CAkKRSSs.js": { + "file": "_app/immutable/chunks/CAkKRSSs.js", + "name": "input", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "_CvGSsBh3.js": { + "file": "_app/immutable/chunks/CvGSsBh3.js", + "name": "index-client", + "imports": [ + "_BxEwC6Zc.js", + "_DoLu9QLy.js" + ] + }, + "_Cy2QBcXt.js": { + "file": "_app/immutable/chunks/Cy2QBcXt.js", + "name": "select", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "_CyUoLS3q.js": { + "file": "_app/immutable/chunks/CyUoLS3q.js", + "name": "snippet", + "imports": [ + "_BxEwC6Zc.js", + "_kALFGdUG.js" + ] + }, + "_D2SGgx9j.js": { + "file": "_app/immutable/chunks/D2SGgx9j.js", + "name": "index", + "imports": [ + "_DAtZQVjv.js", + "_BkIq08el.js" + ] + }, + "_DAtZQVjv.js": { + "file": "_app/immutable/chunks/DAtZQVjv.js", + "name": "state.svelte", + "imports": [ + "_CvGSsBh3.js", + "_BxEwC6Zc.js" + ] + }, + "_DoLu9QLy.js": { + "file": "_app/immutable/chunks/DoLu9QLy.js", + "name": "render", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "_DsnmJJEf.js": { + "file": "_app/immutable/chunks/DsnmJJEf.js", + "name": "disclose-version" + }, + "_TpZZa089.js": { + "file": "_app/immutable/chunks/TpZZa089.js", + "name": "legacy", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "_kALFGdUG.js": { + "file": "_app/immutable/chunks/kALFGdUG.js", + "name": "branches", + "imports": [ + "_BxEwC6Zc.js" + ] + }, + "node_modules/@sveltejs/kit/src/runtime/client/entry.js": { + "file": "_app/immutable/entry/start.Df1GVxyt.js", + "name": "entry/start", + "src": "node_modules/@sveltejs/kit/src/runtime/client/entry.js", + "isEntry": true, + "imports": [ + "_BkIq08el.js", + "_DAtZQVjv.js" + ] + } +} \ No newline at end of file diff --git a/.svelte-kit/output/client/_app/immutable/assets/2.Dv2no-8m.css b/.svelte-kit/output/client/_app/immutable/assets/2.Dv2no-8m.css new file mode 100644 index 0000000..d32e9ae --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/assets/2.Dv2no-8m.css @@ -0,0 +1 @@ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:"Geist Mono", ui-monospace, monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-500:oklch(65.6% .241 354.308);--color-gray-300:oklch(87.2% .01 258.338);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-6xl:3.75rem;--text-6xl--line-height:1;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--blur-md:12px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface-900:#0a0a0f;--color-surface-800:#12121a;--color-surface-700:#1a1a25;--color-surface-600:#24243a;--color-accent:#ff66ab;--color-accent-hover:#ff85be;--color-accent-dim:#ff66ab33;--color-text-primary:#eeeef0;--color-text-secondary:#8888a0;--color-border:#2a2a3d;--font-body:"Geist", system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.mx-auto{margin-inline:auto}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-56{margin-left:calc(var(--spacing) * 56)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.table{display:table}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-full{height:100%}.max-h-\[80vh\]{max-height:80vh}.min-h-\[70vh\]{min-height:70vh}.min-h-dvh{min-height:100dvh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.rotate-90{rotate:90deg}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[auto_1fr_auto_auto_auto\]{grid-template-columns:auto 1fr auto auto auto}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent\/20{border-color:#ff66ab33}@supports (color:color-mix(in lab,red,red)){.border-accent\/20{border-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.border-accent\/30{border-color:#ff66ab4d}@supports (color:color-mix(in lab,red,red)){.border-accent\/30{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/30{border-color:color-mix(in oklab,var(--color-blue-500) 30%,transparent)}}.border-border{border-color:var(--color-border)}.border-border\/50{border-color:#2a2a3d80}@supports (color:color-mix(in lab,red,red)){.border-border\/50{border-color:color-mix(in oklab,var(--color-border) 50%,transparent)}}.border-gray-300{border-color:var(--color-gray-300)}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.border-green-500\/30{border-color:#00c7584d}@supports (color:color-mix(in lab,red,red)){.border-green-500\/30{border-color:color-mix(in oklab,var(--color-green-500) 30%,transparent)}}.border-green-500\/40{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.border-green-500\/40{border-color:color-mix(in oklab,var(--color-green-500) 40%,transparent)}}.border-pink-500\/30{border-color:#f6339a4d}@supports (color:color-mix(in lab,red,red)){.border-pink-500\/30{border-color:color-mix(in oklab,var(--color-pink-500) 30%,transparent)}}.border-purple-500\/30{border-color:#ac4bff4d}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/30{border-color:color-mix(in oklab,var(--color-purple-500) 30%,transparent)}}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500) 30%,transparent)}}.border-yellow-500\/30{border-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/30{border-color:color-mix(in oklab,var(--color-yellow-500) 30%,transparent)}}.bg-accent{background-color:var(--color-accent)}.bg-accent-dim{background-color:var(--color-accent-dim)}.bg-accent\/10{background-color:#ff66ab1a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500) 10%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.bg-blue-600{background-color:var(--color-blue-600)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500\/5{background-color:#00c7580d}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/5{background-color:color-mix(in oklab,var(--color-green-500) 5%,transparent)}}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/20{background-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.bg-pink-500\/20{background-color:#f6339a33}@supports (color:color-mix(in lab,red,red)){.bg-pink-500\/20{background-color:color-mix(in oklab,var(--color-pink-500) 20%,transparent)}}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500) 10%,transparent)}}.bg-purple-500\/20{background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/20{background-color:color-mix(in oklab,var(--color-purple-500) 20%,transparent)}}.bg-red-500\/5{background-color:#fb2c360d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/5{background-color:color-mix(in oklab,var(--color-red-500) 5%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.bg-surface-600{background-color:var(--color-surface-600)}.bg-surface-700{background-color:var(--color-surface-700)}.bg-surface-800{background-color:var(--color-surface-800)}.bg-surface-900{background-color:var(--color-surface-900)}.bg-surface-900\/80{background-color:#0a0a0fcc}@supports (color:color-mix(in lab,red,red)){.bg-surface-900\/80{background-color:color-mix(in oklab,var(--color-surface-900) 80%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-yellow-500\/5{background-color:#edb2000d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/5{background-color:color-mix(in oklab,var(--color-yellow-500) 5%,transparent)}}.bg-yellow-500\/20{background-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/20{background-color:color-mix(in oklab,var(--color-yellow-500) 20%,transparent)}}.object-cover{object-fit:cover}.p-1{padding:calc(var(--spacing) * 1)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-accent{color:var(--color-accent)}.text-blue-400{color:var(--color-blue-400)}.text-green-400{color:var(--color-green-400)}.text-pink-400{color:var(--color-pink-400)}.text-purple-400{color:var(--color-purple-400)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-surface-900{color:var(--color-surface-900)}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-text-secondary\/30{color:#8888a04d}@supports (color:color-mix(in lab,red,red)){.text-text-secondary\/30{color:color-mix(in oklab,var(--color-text-secondary) 30%,transparent)}}.text-white{color:var(--color-white)}.text-yellow-400{color:var(--color-yellow-400)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.group-hover\:text-accent:is(:where(.group):hover *){color:var(--color-accent)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-text-secondary\/50::placeholder{color:#8888a080}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-text-secondary\/50::placeholder{color:color-mix(in oklab,var(--color-text-secondary) 50%,transparent)}}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:border-accent:hover{border-color:var(--color-accent)}.hover\:border-accent\/30:hover{border-color:#ff66ab4d}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/30:hover{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.hover\:border-accent\/40:hover{border-color:#ff66ab66}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/40:hover{border-color:color-mix(in oklab,var(--color-accent) 40%,transparent)}}.hover\:border-green-500\/40:hover{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.hover\:border-green-500\/40:hover{border-color:color-mix(in oklab,var(--color-green-500) 40%,transparent)}}.hover\:border-red-500\/30:hover{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.hover\:border-red-500\/30:hover{border-color:color-mix(in oklab,var(--color-red-500) 30%,transparent)}}.hover\:bg-accent-hover:hover{background-color:var(--color-accent-hover)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.hover\:bg-surface-600:hover{background-color:var(--color-surface-600)}.hover\:bg-surface-700:hover{background-color:var(--color-surface-700)}.hover\:bg-yellow-500\/10:hover{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-500\/10:hover{background-color:color-mix(in oklab,var(--color-yellow-500) 10%,transparent)}}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:64rem){.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}html{background-color:var(--color-surface-900);color:var(--color-text-primary);font-family:var(--font-body)}body{min-height:100dvh}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:var(--color-surface-900)}::-webkit-scrollbar-thumb{background:var(--color-surface-600);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--color-accent)}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}} diff --git a/.svelte-kit/output/client/_app/immutable/chunks/BAocuFgQ.js b/.svelte-kit/output/client/_app/immutable/chunks/BAocuFgQ.js new file mode 100644 index 0000000..4fefbc4 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/BAocuFgQ.js @@ -0,0 +1 @@ +import{q as h,C as w,D as A,am as E,ap as L}from"./BxEwC6Zc.js";import{i as x,a as T,p as k,b as D}from"./BkIq08el.js";function C(t,r,s){h(()=>{var n=w(()=>r(t,s?.())||{});if(s&&n?.update){var a=!1,o={};A(()=>{var e=s();E(e),a&&L(o,e)&&(o=e,n.update(e))}),a=!0}if(n?.destroy)return()=>n.destroy()})}function H(t){const r=JSON.parse(t);return r.data&&(r.data=k(r.data,D.decoders)),r}function d(t){return HTMLElement.prototype.cloneNode.call(t)}function S(t,r=()=>{}){const s=async({action:a,result:o,reset:e=!0,invalidateAll:c=!0})=>{o.type==="success"&&(e&&HTMLFormElement.prototype.reset.call(t),c&&await x()),(location.origin+location.pathname===a.origin+a.pathname||o.type==="redirect"||o.type==="error")&&await T(o)};async function n(a){if((a.submitter?.hasAttribute("formmethod")?a.submitter.formMethod:d(t).method)!=="post")return;a.preventDefault();const e=new URL(a.submitter?.hasAttribute("formaction")?a.submitter.formAction:d(t).action),c=a.submitter?.hasAttribute("formenctype")?a.submitter.formEnctype:d(t).enctype,p=new FormData(t,a.submitter),u=new AbortController;let m=!1;const y=await r({action:e,cancel:()=>m=!0,controller:u,formData:p,formElement:t,submitter:a.submitter})??s;if(m)return;let l;try{const i=new Headers({accept:"application/json","x-sveltekit-action":"true"});c!=="multipart/form-data"&&i.set("Content-Type",/^(:?application\/x-www-form-urlencoded|text\/plain)$/.test(c)?c:"application/x-www-form-urlencoded");const b=c==="multipart/form-data"?p:new URLSearchParams(p),f=await fetch(e,{method:"POST",headers:i,cache:"no-store",body:b,signal:u.signal});l=H(await f.text()),l.type==="error"&&(l.status=f.status)}catch(i){if(i?.name==="AbortError")return;l={type:"error",error:i}}await y({action:e,formData:p,formElement:t,update:i=>s({action:e,result:l,reset:i?.reset,invalidateAll:i?.invalidateAll}),result:l})}return HTMLFormElement.prototype.addEventListener.call(t,"submit",n),{destroy(){HTMLFormElement.prototype.removeEventListener.call(t,"submit",n)}}}export{C as a,S as e}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/BLQQas77.js b/.svelte-kit/output/client/_app/immutable/chunks/BLQQas77.js new file mode 100644 index 0000000..a308712 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/BLQQas77.js @@ -0,0 +1 @@ +import{G as b,h as Z,H as J,F as H,I as D,J as $,K as y,k as q,L as j,M as ee,N as B,O as F,P as O,Q as re,R as ne,S as U,z as ae,T as m,U as L,V as fe,W as ie,X as le,w as ue,Y,Z as se,_ as oe,$ as te,a0 as X,a1 as ve,a2 as K,a3 as P,a4 as z,a5 as de,a6 as ce,a7 as pe,a8 as _e,a9 as he,aa as ge}from"./BxEwC6Zc.js";function Ce(e,i){return i}function Ee(e,i,l){for(var d=[],g=i.length,s,u=i.length,p=0;p{if(s){if(s.pending.delete(E),s.done.add(E),s.pending.size===0){var o=e.outrogroups;V(Y(s.done)),o.delete(s),o.size===0&&(e.outrogroups=null)}}else u-=1},!1)}if(u===0){var f=d.length===0&&l!==null;if(f){var t=l,n=t.parentNode;_e(n),n.append(t),e.items.clear()}V(i,!f)}else s={pending:new Set(i),done:new Set},(e.outrogroups??=new Set).add(s)}function V(e,i=!0){for(var l=0;l{var a=l();return ue(a)?a:a==null?[]:Y(a)}),o,c=!0;function A(){r.fallback=n,me(r,o,u,i,d),n!==null&&(o.length===0?(n.f&m)===0?K(n):(n.f^=m,k(n,null,u)):P(n,()=>{n=null}))}var I=Z(()=>{o=q(E);var a=o.length;let N=!1;if(H){var S=j(u)===ee;S!==(a===0)&&(u=B(),D(u),F(!1),N=!0)}for(var h=new Set,C=ae,x=ie(),_=0;_s(u)):(n=L(()=>s(G??=b())),n.f|=m)),a>h.size&&fe(),H&&a>0&&D(B()),!c)if(x){for(const[Q,W]of p)h.has(Q)||C.skip_effect(W.e);C.oncommit(A),C.ondiscard(()=>{})}else A();N&&F(!0),q(E)}),r={effect:I,items:p,outrogroups:null,fallback:n};c=!1,H&&(u=O)}function M(e){for(;e!==null&&(e.f&ce)===0;)e=e.next;return e}function me(e,i,l,d,g){var s=(d&pe)!==0,u=i.length,p=e.items,f=M(e.effect.first),t,n=null,E,o=[],c=[],A,I,r,a;if(s)for(a=0;a0){var R=(d&J)!==0&&u===0?l:null;if(s){for(a=0;a{if(E!==void 0)for(r of E)r.nodes?.a?.apply()})}function Te(e,i,l,d,g,s,u,p){var f=(u&se)!==0?(u&oe)===0?te(l,!1,!1):X(l):null,t=(u&ve)!==0?X(g):null;return{v:f,i:t,e:L(()=>(s(i,f??l,t??g,p),()=>{e.delete(d)}))}}function k(e,i,l){if(e.nodes)for(var d=e.nodes.start,g=e.nodes.end,s=i&&(i.f&m)===0?i.nodes.start:l;d!==null;){var u=ge(d);if(s.before(d),d===g)return;d=u}}function T(e,i,l){i===null?e.effect.first=l:i.next=l,l===null?e.effect.last=i:l.prev=i}export{we as e,Ce as i}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/BQKoxf2Q.js b/.svelte-kit/output/client/_app/immutable/chunks/BQKoxf2Q.js new file mode 100644 index 0000000..1911957 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/BQKoxf2Q.js @@ -0,0 +1 @@ +import{h as b,F as f,K as h,E as p,L as v,aP as g,M as m,N as A,I as E,O as _,P as R}from"./BxEwC6Zc.js";import{B as S}from"./kALFGdUG.js";function O(l,u,c=!1){var t;f&&(t=R,h());var n=new S(l),T=c?p:0;function i(a,e){if(f){var r=v(t),s;if(r===g?s=0:r===m?s=!1:s=parseInt(r.substring(1)),a!==s){var o=A();E(o),n.anchor=o,_(!1),n.ensure(a,e),_(!0);return}}n.ensure(a,e)}b(()=>{var a=!1;u((e,r=0)=>{a=!0,i(r,e)}),a||i(!1,null)},T)}const I=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"}));export{I as _,O as i}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/BkIq08el.js b/.svelte-kit/output/client/_app/immutable/chunks/BkIq08el.js new file mode 100644 index 0000000..609fb01 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/BkIq08el.js @@ -0,0 +1,3 @@ +import{s as ft}from"./CvGSsBh3.js";import{a as ut,e as dt,c as ht,H as B,N as G,d as Ve,i as Ae,b as L,s as x,p as R,n as ge,f as Ke,g as ye,h as ee,j as pe,S as Ye,P as pt,u as Se,k as _t,l as mt,o as He,m as V,q as gt,t as yt,v as Ge,w as wt,x as vt,y as ze,z as bt,A as xe}from"./DAtZQVjv.js";import{B as te,as as Et}from"./BxEwC6Zc.js";class fe{constructor(t,a){this.status=t,typeof a=="string"?this.body={message:a}:a?this.body=a:this.body={message:`Error: ${t}`}}toString(){return JSON.stringify(this.body)}}class Re{constructor(t,a){this.status=t,this.location=a}}class Ie extends Error{constructor(t,a,n){super(n),this.status=t,this.text=a}}function kt({nodes:e,server_loads:t,dictionary:a,matchers:n}){const r=new Set(t);return Object.entries(a).map(([c,[i,l,h]])=>{const{pattern:d,params:m}=ut(c),u={id:c,exec:f=>{const y=d.exec(f);if(y)return dt(y,m,n)},errors:[1,...h||[]].map(f=>e[f]),layouts:[0,...l||[]].map(s),leaf:o(i)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function o(c){const i=c<0;return i&&(c=~c),[i,e[c]]}function s(c){return c===void 0?c:[r.has(c),e[c]]}}function Je(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function Be(e,t,a=JSON.stringify){const n=a(t);try{sessionStorage[e]=n}catch{}}function At(e){const t=Rt(e),a=new ArrayBuffer(t.length),n=new DataView(a);for(let r=0;r>16),t+=String.fromCharCode((a&65280)>>8),t+=String.fromCharCode(a&255),a=n=0);return n===12?(a>>=4,t+=String.fromCharCode(a)):n===18&&(a>>=2,t+=String.fromCharCode((a&65280)>>8),t+=String.fromCharCode(a&255)),t}const It=-1,Tt=-2,Lt=-3,Pt=-4,Ut=-5,Nt=-6,Ot=-7;function oa(e,t){return We(JSON.parse(e),t)}function We(e,t){if(typeof e=="number")return o(e,!0);if(!Array.isArray(e)||e.length===0)throw new Error("Invalid input");const a=e,n=Array(a.length);let r=null;function o(s,c=!1){if(s===It)return;if(s===Lt)return NaN;if(s===Pt)return 1/0;if(s===Ut)return-1/0;if(s===Nt)return-0;if(c||typeof s!="number")throw new Error("Invalid input");if(s in n)return n[s];const i=a[s];if(!i||typeof i!="object")n[s]=i;else if(Array.isArray(i))if(typeof i[0]=="string"){const l=i[0],h=t&&Object.hasOwn(t,l)?t[l]:void 0;if(h){let d=i[1];if(typeof d!="number"&&(d=a.push(i[1])-1),r??=new Set,r.has(d))throw new Error("Invalid circular reference");return r.add(d),n[s]=h(o(d)),r.delete(d),n[s]}switch(l){case"Date":n[s]=new Date(i[1]);break;case"Set":const d=new Set;n[s]=d;for(let f=1;ft!=null)}const jt="x-sveltekit-invalidated",Dt="x-sveltekit-trailing-slash";function ae(e){return e instanceof fe||e instanceof Ie?e.status:500}function xt(e){return e instanceof Ie?e.text:"Internal Error"}const Bt="/__data.json",Ft=".html__data.json";function Mt(e){return e.endsWith(".html")?e.replace(/\.html$/,Ft):e.replace(/\/$/,"")+Bt}const{tick:Xe}=ft,$t=new Set(["icon","shortcut icon","apple-touch-icon"]),D=Je(ze)??{},z=Je(Ge)??{},j={url:xe({}),page:xe({}),navigating:bt(null),updated:ht()};function Te(e){D[e]=x()}function qt(e,t){let a=e+1;for(;D[a];)delete D[a],a+=1;for(a=t+1;z[a];)delete z[a],a+=1}function $(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise(()=>{})}async function Ze(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration(L||"/");e&&await e.update()}}function we(){}let Le,ve,ne,N,be,E;const re=[],oe=[];let P=null;function se(){P?.fork?.then(e=>e?.discard()),P=null}const Q=new Map,Qe=new Set,Vt=new Set,Y=new Set;let p={branch:[],error:null,url:null},Pe=!1,ie=!1,Fe=!0,J=!1,K=!1,et=!1,X=!1,q,S,I,O;const H=new Set;let _e;const ce=new Map;async function sa(e,t,a){globalThis.__sveltekit_12t8tv8?.data&&globalThis.__sveltekit_12t8tv8.data,document.URL!==location.href&&(location.href=location.href),E=e,await e.hooks.init?.(),Le=kt(e),N=document.documentElement,be=t,ve=e.nodes[0],ne=e.nodes[1],ve(),ne(),S=history.state?.[B],I=history.state?.[G],S||(S=I=Date.now(),history.replaceState({...history.state,[B]:S,[G]:I},""));const n=D[S];function r(){n&&(history.scrollRestoration="manual",scrollTo(n.x,n.y))}a?(r(),await ea(be,a)):(await F({type:"enter",url:Ve(E.hash?ta(new URL(location.href)):location.href),replace_state:!0}),r()),Qt()}async function Kt(e=!0,t=!0){if(await(_e||=Promise.resolve()),!_e)return;_e=null;const a=O={},n=await Z(p.url,!0);if(se(),X&&ce.forEach(({resource:r})=>{r.refresh?.()}),e){const r=R.state,o=n&&await Ce(n);if(!o||a!==O)return;if(o.type==="redirect")return Ue(new URL(o.location,p.url).href,{replaceState:!0},1,a);t||(o.props.page.state=r),Se(o.props.page),p=o.state,Ee(),q.$set(o.props)}else Ee();await Promise.all([...ce.values()].map(({resource:r})=>r)).catch(we)}function Ee(){re.length=0,X=!1}function tt(e){oe.some(t=>t?.snapshot)&&(z[e]=oe.map(t=>t?.snapshot?.capture()))}function at(e){z[e]?.forEach((t,a)=>{oe[a]?.snapshot?.restore(t)})}function Me(){Te(S),Be(ze,D),tt(I),Be(Ge,z)}async function Ue(e,t,a,n){let r;t.invalidateAll&&se(),await F({type:"goto",url:Ve(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:a,nav_token:n,accept:()=>{t.invalidateAll&&(X=!0,r=[...ce.keys()]),t.invalidate&&t.invalidate.forEach(Xt)}}),t.invalidateAll&&te().then(te).then(()=>{ce.forEach(({resource:o},s)=>{r?.includes(s)&&o.refresh?.()})})}async function Yt(e){if(e.id!==P?.id){se();const t={};H.add(t),P={id:e.id,token:t,promise:Ce({...e,preload:t}).then(a=>(H.delete(t),a.type==="loaded"&&a.state.error&&se(),a)),fork:null}}return P.promise}async function me(e){const t=(await Z(e,!1))?.route;t&&await Promise.all([...t.layouts,t.leaf].filter(Boolean).map(a=>a[1]()))}async function nt(e,t,a){p=e.state;const n=document.querySelector("style[data-sveltekit]");if(n&&n.remove(),Object.assign(R,e.props.page),q=new E.root({target:t,props:{...e.props,stores:j,components:oe},hydrate:a,sync:!1}),await Promise.resolve(),at(I),a){const r={from:null,to:{params:p.params,route:{id:p.route?.id??null},url:new URL(location.href),scroll:D[S]??x()},willUnload:!1,type:"enter",complete:Promise.resolve()};Y.forEach(o=>o(r))}ie=!0}function W({url:e,params:t,branch:a,status:n,error:r,route:o,form:s}){let c="never";if(L&&(e.pathname===L||e.pathname===L+"/"))c="always";else for(const u of a)u?.slash!==void 0&&(c=u.slash);e.pathname=mt(e.pathname,c),e.search=e.search;const i={type:"loaded",state:{url:e,params:t,branch:a,error:r,route:o},props:{constructors:Ct(a).map(u=>u.node.component),page:de(R)}};s!==void 0&&(i.props.form=s);let l={},h=!R,d=0;for(let u=0;uc(new URL(s))))return!0;return!1}function Oe(e,t){return e?.type==="data"?e:e?.type==="skip"?t??null:null}function Gt(e,t){if(!e)return new Set(t.searchParams.keys());const a=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(const n of a){const r=e.searchParams.getAll(n),o=t.searchParams.getAll(n);r.every(s=>o.includes(s))&&o.every(s=>r.includes(s))&&a.delete(n)}return a}function qe({error:e,url:t,route:a,params:n}){return{type:"loaded",state:{error:e,url:t,route:a,params:n,branch:[]},props:{page:de(R),constructors:[]}}}async function Ce({id:e,invalidating:t,url:a,params:n,route:r,preload:o}){if(P?.id===e)return H.delete(P.token),P.promise;const{errors:s,layouts:c,leaf:i}=r,l=[...c,i];s.forEach(g=>g?.().catch(()=>{})),l.forEach(g=>g?.[1]().catch(()=>{}));let h=null;const d=p.url?e!==le(p.url):!1,m=p.route?r.id!==p.route.id:!1,u=Gt(p.url,a);let f=!1;{const g=l.map((w,k)=>{const A=p.branch[k],v=!!w?.[0]&&(A?.loader!==w[1]||$e(f,m,d,u,A.server?.uses,n));return v&&(f=!0),v});if(g.some(Boolean)){try{h=await it(a,g)}catch(w){const k=await M(w,{url:a,params:n,route:{id:e}});return H.has(o)?qe({error:k,url:a,params:n,route:r}):ue({status:ae(w),error:k,url:a,route:r})}if(h.type==="redirect")return h}}const y=h?.nodes;let b=!1;const _=l.map(async(g,w)=>{if(!g)return;const k=p.branch[w],A=y?.[w];if((!A||A.type==="skip")&&g[1]===k?.loader&&!$e(b,m,d,u,k.universal?.uses,n))return k;if(b=!0,A?.type==="error")throw A;return Ne({loader:g[1],url:a,params:n,route:r,parent:async()=>{const C={};for(let U=0;U{});const T=[];for(let g=0;gPromise.resolve({}),server_data_node:Oe(o)}),c={node:await ne(),loader:ne,universal:null,server:null,data:null};return W({url:a,params:r,branch:[s,c],status:e,error:t,route:null})}catch(s){if(s instanceof Re)return Ue(new URL(s.location,location.href),{},0);throw s}}async function zt(e){const t=e.href;if(Q.has(t))return Q.get(t);let a;try{const n=(async()=>{let r=await E.hooks.reroute({url:new URL(e),fetch:async(o,s)=>Ht(o,s,e).promise})??e;if(typeof r=="string"){const o=new URL(e);E.hash?o.hash=r:o.pathname=r,r=o}return r})();Q.set(t,n),a=await n}catch{Q.delete(t);return}return a}async function Z(e,t){if(e&&!Ae(e,L,E.hash)){const a=await zt(e);if(!a)return;const n=Jt(a);for(const r of Le){const o=r.exec(n);if(o)return{id:le(e),invalidating:t,route:r,params:_t(o),url:e}}}}function Jt(e){return gt(E.hash?e.hash.replace(/^#/,"").replace(/[?#].+/,""):e.pathname.slice(L.length))||"/"}function le(e){return(E.hash?e.hash.replace(/^#/,""):e.pathname)+e.search}function ot({url:e,type:t,intent:a,delta:n,event:r,scroll:o}){let s=!1;const c=De(p,a,e,t,o??null);n!==void 0&&(c.navigation.delta=n),r!==void 0&&(c.navigation.event=r);const i={...c.navigation,cancel:()=>{s=!0,c.reject(new Error("navigation cancelled"))}};return J||Qe.forEach(l=>l(i)),s?null:c}async function F({type:e,url:t,popped:a,keepfocus:n,noscroll:r,replace_state:o,state:s={},redirect_count:c=0,nav_token:i={},accept:l=we,block:h=we,event:d}){const m=O;O=i;const u=await Z(t,!1),f=e==="enter"?De(p,u,t,e):ot({url:t,type:e,delta:a?.delta,intent:u,scroll:a?.scroll,event:d});if(!f){h(),O===i&&(O=m);return}const y=S,b=I;l(),J=!0,ie&&f.navigation.type!=="enter"&&j.navigating.set(ge.current=f.navigation);let _=u&&await Ce(u);if(!_){if(Ae(t,L,E.hash))return await $(t,o);_=await st(t,{id:null},await M(new Ie(404,"Not Found",`Not found: ${t.pathname}`),{url:t,params:{},route:{id:null}}),404,o)}if(t=u?.url||t,O!==i)return f.reject(new Error("navigation aborted")),!1;if(_.type==="redirect"){if(c<20){await F({type:e,url:new URL(_.location,t),popped:a,keepfocus:n,noscroll:r,replace_state:o,state:s,redirect_count:c+1,nav_token:i}),f.fulfil(void 0);return}_=await ue({status:500,error:await M(new Error("Redirect loop"),{url:t,params:{},route:{id:null}}),url:t,route:{id:null}})}else _.props.page.status>=400&&await j.updated.check()&&(await Ze(),await $(t,o));if(Ee(),Te(y),tt(b),_.props.page.url.pathname!==t.pathname&&(t.pathname=_.props.page.url.pathname),s=a?a.state:s,!a){const v=o?0:1,C={[B]:S+=v,[G]:I+=v,[Ye]:s};(o?history.replaceState:history.pushState).call(history,C,"",t),o||qt(S,I)}const T=u&&P?.id===u.id?P.fork:null;P=null,_.props.page.state=s;let g;if(ie){const v=(await Promise.all(Array.from(Vt,U=>U(f.navigation)))).filter(U=>typeof U=="function");if(v.length>0){let U=function(){v.forEach(he=>{Y.delete(he)})};v.push(U),v.forEach(he=>{Y.add(he)})}p=_.state,_.props.page&&(_.props.page.url=t);const C=T&&await T;C?g=C.commit():(q.$set(_.props),Se(_.props.page),g=Et?.()),et=!0}else await nt(_,be,!1);const{activeElement:w}=document;await g,await te(),await te();let k=null;if(Fe){const v=a?a.scroll:r?x():null;v?scrollTo(v.x,v.y):(k=t.hash&&document.getElementById(lt(t)))?k.scrollIntoView():scrollTo(0,0)}const A=document.activeElement!==w&&document.activeElement!==document.body;!n&&!A&&je(t,!k),Fe=!0,_.props.page&&Object.assign(R,_.props.page),J=!1,e==="popstate"&&at(I),f.fulfil(void 0),f.navigation.to&&(f.navigation.to.scroll=x()),Y.forEach(v=>v(f.navigation)),j.navigating.set(ge.current=null)}async function st(e,t,a,n,r){return e.origin===He&&e.pathname===location.pathname&&!Pe?await ue({status:n,error:a,url:e,route:t}):await $(e,r)}function Wt(){let e,t={element:void 0,href:void 0},a;N.addEventListener("mousemove",c=>{const i=c.target;clearTimeout(e),e=setTimeout(()=>{o(i,V.hover)},20)});function n(c){c.defaultPrevented||o(c.composedPath()[0],V.tap)}N.addEventListener("mousedown",n),N.addEventListener("touchstart",n,{passive:!0});const r=new IntersectionObserver(c=>{for(const i of c)i.isIntersecting&&(me(new URL(i.target.href)),r.unobserve(i.target))},{threshold:0});async function o(c,i){const l=Ke(c,N),h=l===t.element&&l?.href===t.href&&i>=a;if(!l||h)return;const{url:d,external:m,download:u}=ye(l,L,E.hash);if(m||u)return;const f=ee(l),y=d&&le(p.url)===le(d);if(!(f.reload||y))if(i<=f.preload_data){t={element:l,href:l.href},a=V.tap;const b=await Z(d,!1);if(!b)return;Yt(b)}else i<=f.preload_code&&(t={element:l,href:l.href},a=i,me(d))}function s(){r.disconnect();for(const c of N.querySelectorAll("a")){const{url:i,external:l,download:h}=ye(c,L,E.hash);if(l||h)continue;const d=ee(c);d.reload||(d.preload_code===V.viewport&&r.observe(c),d.preload_code===V.eager&&me(i))}}Y.add(s),s()}function M(e,t){if(e instanceof fe)return e.body;const a=ae(e),n=xt(e);return E.hooks.handleError({error:e,event:t,status:a,message:n})??{message:n}}function Xt(e){if(typeof e=="function")re.push(e);else{const{href:t}=new URL(e,location.href);re.push(a=>a.href===t)}}function ia(){return X=!0,Kt()}async function ca(e){e.type==="error"?await Zt(e.error,e.status):e.type==="redirect"?await Ue(e.location,{invalidateAll:!0},0):(R.form=e.data,R.status=e.status,q.$set({form:null,page:de(R)}),await Xe(),q.$set({form:e.data}),e.type==="success"&&je(R.url))}async function Zt(e,t=500){const a=new URL(location.href),{branch:n,route:r}=p;if(!r)return;const o=await rt(p.branch.length,n,r.errors);if(o){const s=W({url:a,params:p.params,branch:n.slice(0,o.idx).concat(o.node),status:t,error:e,route:r});p=s.state,q.$set(s.props),Se(s.props.page),Xe().then(()=>je(p.url))}}function Qt(){history.scrollRestoration="manual",addEventListener("beforeunload",t=>{let a=!1;if(Me(),!J){const n=De(p,void 0,null,"leave"),r={...n.navigation,cancel:()=>{a=!0,n.reject(new Error("navigation cancelled"))}};Qe.forEach(o=>o(r))}a?(t.preventDefault(),t.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&Me()}),navigator.connection?.saveData||Wt(),N.addEventListener("click",async t=>{if(t.button||t.which!==1||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.defaultPrevented)return;const a=Ke(t.composedPath()[0],N);if(!a)return;const{url:n,external:r,target:o,download:s}=ye(a,L,E.hash);if(!n)return;if(o==="_parent"||o==="_top"){if(window.parent!==window)return}else if(o&&o!=="_self")return;const c=ee(a);if(!(a instanceof SVGAElement)&&n.protocol!==location.protocol&&!(n.protocol==="https:"||n.protocol==="http:")||s)return;const[l,h]=(E.hash?n.hash.replace(/^#/,""):n.href).split("#"),d=l===pe(location);if(r||c.reload&&(!d||!h)){ot({url:n,type:"link",event:t})?J=!0:t.preventDefault();return}if(h!==void 0&&d){const[,m]=p.url.href.split("#");if(m===h){if(t.preventDefault(),h===""||h==="top"&&a.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const u=a.ownerDocument.getElementById(decodeURIComponent(h));u&&(u.scrollIntoView(),u.focus())}return}if(K=!0,Te(S),e(n),!c.replace_state)return;K=!1}t.preventDefault(),await new Promise(m=>{requestAnimationFrame(()=>{setTimeout(m,0)}),setTimeout(m,100)}),await F({type:"link",url:n,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??n.href===location.href,event:t})}),N.addEventListener("submit",t=>{if(t.defaultPrevented)return;const a=HTMLFormElement.prototype.cloneNode.call(t.target),n=t.submitter;if((n?.formTarget||a.target)==="_blank"||(n?.formMethod||a.method)!=="get")return;const s=new URL(n?.hasAttribute("formaction")&&n?.formAction||a.action);if(Ae(s,L,!1))return;const c=t.target,i=ee(c);if(i.reload)return;t.preventDefault(),t.stopPropagation();const l=new FormData(c,n);s.search=new URLSearchParams(l).toString(),F({type:"form",url:s,keepfocus:i.keepfocus,noscroll:i.noscroll,replace_state:i.replace_state??s.href===location.href,event:t})}),addEventListener("popstate",async t=>{if(!ke){if(t.state?.[B]){const a=t.state[B];if(O={},a===S)return;const n=D[a],r=t.state[Ye]??{},o=new URL(t.state[pt]??location.href),s=t.state[G],c=p.url?pe(location)===pe(p.url):!1;if(s===I&&(et||c)){r!==R.state&&(R.state=r),e(o),D[S]=x(),n&&scrollTo(n.x,n.y),S=a;return}const l=a-S;await F({type:"popstate",url:o,popped:{state:r,scroll:n,delta:l},accept:()=>{S=a,I=s},block:()=>{history.go(-l)},nav_token:O,event:t})}else if(!K){const a=new URL(location.href);e(a),E.hash&&location.reload()}}}),addEventListener("hashchange",()=>{K&&(K=!1,history.replaceState({...history.state,[B]:++S,[G]:I},"",location.href))});for(const t of document.querySelectorAll("link"))$t.has(t.rel)&&(t.href=t.href);addEventListener("pageshow",t=>{t.persisted&&j.navigating.set(ge.current=null)});function e(t){p.url=R.url=t,j.page.set(de(R)),j.page.notify()}}async function ea(e,{status:t=200,error:a,node_ids:n,params:r,route:o,server_route:s,data:c,form:i}){Pe=!0;const l=new URL(location.href);let h;({params:r={},route:o={id:null}}=await Z(l,!1)||{}),h=Le.find(({id:u})=>u===o.id);let d,m=!0;try{const u=n.map(async(y,b)=>{const _=c[b];return _?.uses&&(_.uses=ct(_.uses)),Ne({loader:E.nodes[y],url:l,params:r,route:o,parent:async()=>{const T={};for(let g=0;go?"1":"0").join(""));const n=window.fetch,r=await n(a.href,{});if(!r.ok){let o;throw r.headers.get("content-type")?.includes("application/json")?o=await r.json():r.status===404?o="Not Found":r.status===500&&(o="Internal Error"),new fe(r.status,o)}return new Promise(async o=>{const s=new Map,c=r.body.getReader();function i(h){return We(h,{...E.decoders,Promise:d=>new Promise((m,u)=>{s.set(d,{fulfil:m,reject:u})})})}let l="";for(;;){const{done:h,value:d}=await c.read();if(h&&!l)break;for(l+=!d&&l?` +`:yt.decode(d,{stream:!0});;){const m=l.indexOf(` +`);if(m===-1)break;const u=JSON.parse(l.slice(0,m));if(l=l.slice(m+1),u.type==="redirect")return o(u);if(u.type==="data")u.nodes?.forEach(f=>{f?.type==="data"&&(f.uses=ct(f.uses),f.data=i(f.data))}),o(u);else if(u.type==="chunk"){const{id:f,data:y,error:b}=u,_=s.get(f);s.delete(f),b?_.reject(i(b)):_.fulfil(i(y))}}}})}function ct(e){return{dependencies:new Set(e?.dependencies??[]),params:new Set(e?.params??[]),parent:!!e?.parent,route:!!e?.route,url:!!e?.url,search_params:new Set(e?.search_params??[])}}let ke=!1;function je(e,t=!0){const a=document.querySelector("[autofocus]");if(a)a.focus();else{const n=lt(e);if(n&&document.getElementById(n)){const{x:o,y:s}=x();setTimeout(()=>{const c=history.state;ke=!0,location.replace(new URL(`#${n}`,location.href)),history.replaceState(c,"",e),t&&scrollTo(o,s),ke=!1})}else{const o=document.body,s=o.getAttribute("tabindex");o.tabIndex=-1,o.focus({preventScroll:!0,focusVisible:!1}),s!==null?o.setAttribute("tabindex",s):o.removeAttribute("tabindex")}const r=getSelection();if(r&&r.type!=="None"){const o=[];for(let s=0;s{if(r.rangeCount===o.length){for(let s=0;s{o=l,s=h});return c.catch(()=>{}),{navigation:{from:{params:e.params,route:{id:e.route?.id??null},url:e.url,scroll:x()},to:a&&{params:t?.params??null,route:{id:t?.route?.id??null},url:a,scroll:r},willUnload:!t,type:n,complete:c},fulfil:o,reject:s}}function de(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function ta(e){const t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function lt(e){let t;if(E.hash){const[,,a]=e.hash.split("#",3);t=a??""}else t=e.hash.slice(1);return decodeURIComponent(t)}export{ca as a,E as b,sa as c,ia as i,oa as p,j as s}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/BnX5ZnVA.js b/.svelte-kit/output/client/_app/immutable/chunks/BnX5ZnVA.js new file mode 100644 index 0000000..af55481 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/BnX5ZnVA.js @@ -0,0 +1 @@ +import{ac as u,ad as v,F as _,ae as h,af as f,a5 as A,ag as l,ah as g}from"./BxEwC6Zc.js";const S=Symbol("is custom element"),p=Symbol("is html"),N=f?"link":"LINK",T=f?"progress":"PROGRESS";function M(s){if(_){var r=!1,e=()=>{if(!r){if(r=!0,s.hasAttribute("value")){var a=s.value;c(s,"value",null),s.value=a}if(s.hasAttribute("checked")){var t=s.checked;c(s,"checked",null),s.checked=t}}};s.__on_r=e,A(e),l()}}function k(s,r){var e=d(s);e.value===(e.value=r??void 0)||s.value===r&&(r!==0||s.nodeName!==T)||(s.value=r??"")}function y(s,r){r?s.hasAttribute("selected")||s.setAttribute("selected",""):s.removeAttribute("selected")}function c(s,r,e,a){var t=d(s);_&&(t[r]=s.getAttribute(r),r==="src"||r==="srcset"||r==="href"&&s.nodeName===N)||t[r]!==(t[r]=e)&&(r==="loading"&&(s[g]=e),e==null?s.removeAttribute(r):typeof e!="string"&&E(s).includes(r)?s[r]=e:s.setAttribute(r,e))}function d(s){return s.__attributes??={[S]:s.nodeName.includes("-"),[p]:s.namespaceURI===u}}var i=new Map;function E(s){var r=s.getAttribute("is")||s.nodeName,e=i.get(r);if(e)return e;i.set(r,e=[]);for(var a,t=s,n=Element.prototype;n!==t;){a=h(t);for(var o in a)a[o].set&&e.push(o);t=v(t)}return e}export{k as a,y as b,M as r,c as s}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/BqOsL1Vv.js b/.svelte-kit/output/client/_app/immutable/chunks/BqOsL1Vv.js new file mode 100644 index 0000000..3f264a6 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/BqOsL1Vv.js @@ -0,0 +1 @@ +import{F as i}from"./BxEwC6Zc.js";function n(s,l,r){var t=s==null?"":""+s;return t===""?null:t}function h(s,l,r,t,f,c){var a=s.__className;if(i||a!==r||a===void 0){var e=n(r);(!i||e!==s.getAttribute("class"))&&(e==null?s.removeAttribute("class"):s.className=e),s.__className=r}return c}export{h as s}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/BxEwC6Zc.js b/.svelte-kit/output/client/_app/immutable/chunks/BxEwC6Zc.js new file mode 100644 index 0000000..7c1d4c2 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/BxEwC6Zc.js @@ -0,0 +1 @@ +var Vt=Array.isArray,Ut=Array.prototype.indexOf,le=Array.prototype.includes,qn=Array.from,Vn=Object.defineProperty,de=Object.getOwnPropertyDescriptor,Bt=Object.getOwnPropertyDescriptors,Gt=Object.prototype,zt=Array.prototype,ft=Object.getPrototypeOf,Ze=Object.isExtensible;const Kt=()=>{};function Un(e){return e()}function Xt(e){for(var t=0;t{e=r,t=s});return{promise:n,resolve:e,reject:t}}function Bn(e,t){if(Array.isArray(e))return e;if(!(Symbol.iterator in e))return Array.from(e);const n=[];for(const r of e)if(n.push(r),n.length===t)break;return n}const g=2,we=4,me=8,ot=1<<24,q=16,L=32,fe=64,$t=128,O=512,m=1024,A=2048,F=4096,M=8192,U=16384,z=32768,Re=65536,Je=1<<17,ut=1<<18,Ee=1<<19,ct=1<<20,Gn=1<<25,ne=65536,Ce=1<<21,Ye=1<<22,B=1<<23,Q=Symbol("$state"),zn=Symbol("legacy props"),Kn=Symbol(""),W=new class extends Error{name="StaleReactionError";message="The reaction that called `getAbortSignal()` was re-run or destroyed"},$n=!!globalThis.document?.contentType&&globalThis.document.contentType.includes("xml"),ge=3,_t=8;function Wt(){throw new Error("https://svelte.dev/e/async_derived_orphan")}function Wn(e,t,n){throw new Error("https://svelte.dev/e/each_key_duplicate")}function Zt(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function Jt(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function Qt(e){throw new Error("https://svelte.dev/e/effect_orphan")}function en(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function Zn(){throw new Error("https://svelte.dev/e/hydration_failed")}function Jn(e){throw new Error("https://svelte.dev/e/props_invalid_value")}function tn(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function nn(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function rn(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function Qn(){throw new Error("https://svelte.dev/e/svelte_boundary_reset_onerror")}const er=1,tr=2,nr=4,rr=8,sr=16,ir=1,ar=2,fr=4,lr=8,or=16,sn=1,an=2,fn="[",ln="[!",ur="[?",on="]",qe={},E=Symbol(),un="http://www.w3.org/1999/xhtml";function Ve(e){console.warn("https://svelte.dev/e/hydration_mismatch")}function cr(){console.warn("https://svelte.dev/e/select_multiple_invalid_value")}function _r(){console.warn("https://svelte.dev/e/svelte_boundary_reset_noop")}let C=!1;function vr(e){C=e}let y;function re(e){if(e===null)throw Ve(),qe;return y=e}function cn(){return re(X(y))}function dr(e){if(C){if(X(y)!==null)throw Ve(),qe;y=e}}function hr(e=1){if(C){for(var t=e,n=y;t--;)n=X(n);y=n}}function pr(e=!0){for(var t=0,n=y;;){if(n.nodeType===_t){var r=n.data;if(r===on){if(t===0)return n;t-=1}else(r===fn||r===ln||r[0]==="["&&!isNaN(Number(r.slice(1))))&&(t+=1)}var s=X(n);e&&n.remove(),n=s}}function yr(e){if(!e||e.nodeType!==_t)throw Ve(),qe;return e.data}function vt(e){return e===this.v}function _n(e,t){return e!=e?t==t:e!==t||e!==null&&typeof e=="object"||typeof e=="function"}function dt(e){return!_n(e,this.v)}let Oe=!1;function wr(){Oe=!0}let T=null;function xe(e){T=e}function mr(e,t=!1,n){T={p:T,i:!1,c:null,e:null,s:e,x:null,l:Oe&&!t?{s:null,u:null,$:[]}:null}}function Er(e){var t=T,n=t.e;if(n!==null){t.e=null;for(var r of n)kt(r)}return t.i=!0,T=t.p,{}}function be(){return!Oe||T!==null&&T.l===null}let Z=[];function ht(){var e=Z;Z=[],Xt(e)}function Qe(e){if(Z.length===0&&!he){var t=Z;queueMicrotask(()=>{t===Z&&ht()})}Z.push(e)}function vn(){for(;Z.length>0;)ht()}function dn(e){var t=d;if(t===null)return v.f|=B,e;if((t.f&z)===0&&(t.f&we)===0)throw e;Ne(e,t)}function Ne(e,t){for(;t!==null;){if((t.f&$t)!==0){if((t.f&z)===0)throw e;try{t.b.error(e);return}catch(n){e=n}}t=t.parent}throw e}const hn=-7169;function w(e,t){e.f=e.f&hn|t}function Ue(e){(e.f&O)!==0||e.deps===null?w(e,m):w(e,F)}function pt(e){if(e!==null)for(const t of e)(t.f&g)===0||(t.f&ne)===0||(t.f^=ne,pt(t.deps))}function pn(e,t,n){(e.f&A)!==0?t.add(e):(e.f&F)!==0&&n.add(e),pt(e.deps),w(e,m)}const Ae=new Set;let p=null,et=null,I=null,S=[],ke=null,Me=!1,he=!1;class ce{current=new Map;previous=new Map;#r=new Set;#s=new Set;#n=0;#i=0;#l=null;#a=new Set;#t=new Set;#e=new Map;is_fork=!1;#f=!1;#o(){return this.is_fork||this.#i>0}skip_effect(t){this.#e.has(t)||this.#e.set(t,{d:[],m:[]})}unskip_effect(t){var n=this.#e.get(t);if(n){this.#e.delete(t);for(var r of n.d)w(r,A),Y(r);for(r of n.m)w(r,F),Y(r)}}process(t){S=[],this.apply();var n=[],r=[];for(const s of t)this.#u(s,n,r);if(this.#o()){this.#c(r),this.#c(n);for(const[s,i]of this.#e)Et(s,i)}else{for(const s of this.#r)s();this.#r.clear(),this.#n===0&&this.#_(),et=this,p=null,tt(r),tt(n),this.#a.clear(),this.#t.clear(),et=null,this.#l?.resolve()}I=null}#u(t,n,r){t.f^=m;for(var s=t.first;s!==null;){var i=s.f,l=(i&(L|fe))!==0,f=l&&(i&m)!==0,a=f||(i&M)!==0||this.#e.has(s);if(!a&&s.fn!==null){l?s.f^=m:(i&we)!==0?n.push(s):Te(s)&&((i&q)!==0&&this.#t.add(s),ue(s));var o=s.first;if(o!==null){s=o;continue}}for(;s!==null;){var c=s.next;if(c!==null){s=c;break}s=s.parent}}}#c(t){for(var n=0;n0){if(yt(),p!==null&&p!==this)return}else this.#n===0&&this.process([]);this.deactivate()}discard(){for(const t of this.#s)t(this);this.#s.clear()}#_(){if(Ae.size>1){this.previous.clear();var t=I,n=!0;for(const s of Ae){if(s===this){n=!1;continue}const i=[];for(const[f,a]of this.current){if(s.current.has(f))if(n&&a!==s.current.get(f))s.current.set(f,a);else continue;i.push(f)}if(i.length===0)continue;const l=[...s.current.keys()].filter(f=>!this.current.has(f));if(l.length>0){var r=S;S=[];const f=new Set,a=new Map;for(const o of i)wt(o,l,f,a);if(S.length>0){p=s,s.apply();for(const o of S)s.#u(o,[],[]);s.deactivate()}S=r}}p=null,I=t}this.#e.clear(),Ae.delete(this)}increment(t){this.#n+=1,t&&(this.#i+=1)}decrement(t){this.#n-=1,t&&(this.#i-=1),!this.#f&&(this.#f=!0,Qe(()=>{this.#f=!1,this.#o()?S.length>0&&this.flush():this.revive()}))}revive(){for(const t of this.#a)this.#t.delete(t),w(t,A),Y(t);for(const t of this.#t)w(t,F),Y(t);this.flush()}oncommit(t){this.#r.add(t)}ondiscard(t){this.#s.add(t)}settled(){return(this.#l??=lt()).promise}static ensure(){if(p===null){const t=p=new ce;Ae.add(p),he||Qe(()=>{p===t&&t.flush()})}return p}apply(){}}function yn(e){var t=he;he=!0;try{for(var n;;){if(vn(),S.length===0&&(p?.flush(),S.length===0))return ke=null,n;yt()}}finally{he=t}}function yt(){Me=!0;var e=null;try{for(var t=0;S.length>0;){var n=ce.ensure();if(t++>1e3){var r,s;wn()}n.process(S),G.clear()}}finally{S=[],Me=!1,ke=null}}function wn(){try{en()}catch(e){Ne(e,ke)}}let H=null;function tt(e){var t=e.length;if(t!==0){for(var n=0;n0)){G.clear();for(const s of H){if((s.f&(U|M))!==0)continue;const i=[s];let l=s.parent;for(;l!==null;)H.has(l)&&(H.delete(l),i.push(l)),l=l.parent;for(let f=i.length-1;f>=0;f--){const a=i[f];(a.f&(U|M))===0&&ue(a)}}H.clear()}}H=null}}function wt(e,t,n,r){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(const s of e.reactions){const i=s.f;(i&g)!==0?wt(s,t,n,r):(i&(Ye|q))!==0&&(i&A)===0&&mt(s,t,r)&&(w(s,A),Y(s))}}function mt(e,t,n){const r=n.get(e);if(r!==void 0)return r;if(e.deps!==null)for(const s of e.deps){if(le.call(t,s))return!0;if((s.f&g)!==0&&mt(s,t,n))return n.set(s,!0),!0}return n.set(e,!1),!1}function Y(e){var t=ke=e,n=t.b;if(n?.is_pending&&(e.f&(we|me|ot))!==0&&(e.f&z)===0){n.defer_effect(e);return}for(;t.parent!==null;){t=t.parent;var r=t.f;if(Me&&t===d&&(r&q)!==0&&(r&ut)===0&&(r&z)!==0)return;if((r&(fe|L))!==0){if((r&m)===0)return;t.f^=m}}S.push(t)}function Et(e,t){if(!((e.f&L)!==0&&(e.f&m)!==0)){(e.f&A)!==0?t.d.push(e):(e.f&F)!==0&&t.m.push(e),w(e,m);for(var n=e.first;n!==null;)Et(n,t),n=n.next}}function mn(e,t,n,r){const s=be()?Be:Tn;var i=e.filter(u=>!u.settled);if(n.length===0&&i.length===0){r(t.map(s));return}var l=d,f=En(),a=i.length===1?i[0].promise:i.length>1?Promise.all(i.map(u=>u.promise)):null;function o(u){f();try{r(u)}catch(_){(l.f&U)===0&&Ne(_,l)}Fe()}if(n.length===0){a.then(()=>o(t.map(s)));return}function c(){f(),Promise.all(n.map(u=>bn(u))).then(u=>o([...t.map(s),...u])).catch(u=>Ne(u,l))}a?a.then(c):c()}function En(){var e=d,t=v,n=T,r=p;return function(i=!0){oe(e),K(t),xe(n),i&&r?.activate()}}function Fe(e=!0){oe(null),K(null),xe(null),e&&p?.deactivate()}function gn(){var e=d.b,t=p,n=e.is_rendered();return e.update_pending_count(1),t.increment(n),()=>{e.update_pending_count(-1),t.decrement(n)}}function Be(e){var t=g|A,n=v!==null&&(v.f&g)!==0?v:null;return d!==null&&(d.f|=Ee),{ctx:T,deps:null,effects:null,equals:vt,f:t,fn:e,reactions:null,rv:0,v:E,wv:0,parent:n??d,ac:null}}function bn(e,t,n){d===null&&Wt();var s=void 0,i=ze(E),l=!v,f=new Map;return In(()=>{var a=lt();s=a.promise;try{Promise.resolve(e()).then(a.resolve,a.reject).finally(Fe)}catch(_){a.reject(_),Fe()}var o=p;if(l){var c=gn();f.get(o)?.reject(W),f.delete(o),f.set(o,a)}const u=(_,b=void 0)=>{if(o.activate(),b)b!==W&&(i.f|=B,je(i,b));else{(i.f&B)!==0&&(i.f^=B),je(i,_);for(const[h,D]of f){if(f.delete(h),h===o)break;D.reject(W)}}c&&c()};a.promise.then(u,_=>u(null,_||"unknown"))}),Dn(()=>{for(const a of f.values())a.reject(W)}),new Promise(a=>{function o(c){function u(){c===s?a(i):o(s)}c.then(u,u)}o(s)})}function gr(e){const t=Be(e);return Mt(t),t}function Tn(e){const t=Be(e);return t.equals=dt,t}function An(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n0&&!Tt&&xn()}return t}function xn(){Tt=!1;for(const e of Le)(e.f&m)!==0&&w(e,F),Te(e)&&ue(e);Le.clear()}function Pe(e){$(e,e.v+1)}function At(e,t){var n=e.reactions;if(n!==null)for(var r=be(),s=n.length,i=0;i{if(ee===i)return f();var a=v,o=ee;K(null),at(i);var c=f();return K(a),at(o),c};return r&&n.set("length",V(e.length)),new Proxy(e,{defineProperty(f,a,o){(!("value"in o)||o.configurable===!1||o.enumerable===!1||o.writable===!1)&&tn();var c=n.get(a);return c===void 0?l(()=>{var u=V(o.value);return n.set(a,u),u}):$(c,o.value,!0),!0},deleteProperty(f,a){var o=n.get(a);if(o===void 0){if(a in f){const c=l(()=>V(E));n.set(a,c),Pe(s)}}else $(o,E),Pe(s);return!0},get(f,a,o){if(a===Q)return e;var c=n.get(a),u=a in f;if(c===void 0&&(!u||de(f,a)?.writable)&&(c=l(()=>{var b=_e(u?f[a]:E),h=V(b);return h}),n.set(a,c)),c!==void 0){var _=ve(c);return _===E?void 0:_}return Reflect.get(f,a,o)},getOwnPropertyDescriptor(f,a){var o=Reflect.getOwnPropertyDescriptor(f,a);if(o&&"value"in o){var c=n.get(a);c&&(o.value=ve(c))}else if(o===void 0){var u=n.get(a),_=u?.v;if(u!==void 0&&_!==E)return{enumerable:!0,configurable:!0,value:_,writable:!0}}return o},has(f,a){if(a===Q)return!0;var o=n.get(a),c=o!==void 0&&o.v!==E||Reflect.has(f,a);if(o!==void 0||d!==null&&(!c||de(f,a)?.writable)){o===void 0&&(o=l(()=>{var _=c?_e(f[a]):E,b=V(_);return b}),n.set(a,o));var u=ve(o);if(u===E)return!1}return c},set(f,a,o,c){var u=n.get(a),_=a in f;if(r&&a==="length")for(var b=o;bV(E)),n.set(b+"",h))}if(u===void 0)(!_||de(f,a)?.writable)&&(u=l(()=>V(void 0)),$(u,_e(o)),n.set(a,u));else{_=u.v!==E;var D=l(()=>_e(o));$(u,D)}var $e=Reflect.getOwnPropertyDescriptor(f,a);if($e?.set&&$e.set.call(c,o),!_){if(r&&typeof a=="string"){var We=n.get("length"),Ie=Number(a);Number.isInteger(Ie)&&Ie>=We.v&&$(We,Ie+1)}Pe(s)}return!0},ownKeys(f){ve(s);var a=Reflect.ownKeys(f).filter(u=>{var _=n.get(u);return _===void 0||_.v!==E});for(var[o,c]of n)c.v!==E&&!(o in f)&&a.push(o);return a},setPrototypeOf(){nn()}})}function nt(e){try{if(e!==null&&typeof e=="object"&&Q in e)return e[Q]}catch{}return e}function Tr(e,t){return Object.is(nt(e),nt(t))}var rt,St,Rt,xt;function Ar(){if(rt===void 0){rt=window,St=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;Rt=de(t,"firstChild").get,xt=de(t,"nextSibling").get,Ze(e)&&(e.__click=void 0,e.__className=void 0,e.__attributes=null,e.__style=void 0,e.__e=void 0),Ze(n)&&(n.__t=void 0)}}function se(e=""){return document.createTextNode(e)}function pe(e){return Rt.call(e)}function X(e){return xt.call(e)}function Sr(e,t){if(!C)return pe(e);var n=pe(y);if(n===null)n=y.appendChild(se());else if(t&&n.nodeType!==ge){var r=se();return n?.before(r),re(r),r}return t&&De(n),re(n),n}function Rr(e,t=!1){if(!C){var n=pe(e);return n instanceof Comment&&n.data===""?X(n):n}if(t){if(y?.nodeType!==ge){var r=se();return y?.before(r),re(r),r}De(y)}return y}function xr(e,t=1,n=!1){let r=C?y:e;for(var s;t--;)s=r,r=X(r);if(!C)return r;if(n){if(r?.nodeType!==ge){var i=se();return r===null?s?.after(i):r.before(i),re(i),i}De(r)}return re(r),r}function Nr(e){e.textContent=""}function Or(){return!1}function Nn(e,t,n){return document.createElementNS(un,e,void 0)}function De(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===ge;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}let st=!1;function On(){st||(st=!0,document.addEventListener("reset",e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(const t of e.target.elements)t.__on_r?.()})},{capture:!0}))}function Ke(e){var t=v,n=d;K(null),oe(null);try{return e()}finally{K(t),oe(n)}}function kr(e,t,n,r=n){e.addEventListener(t,()=>Ke(n));const s=e.__on_r;s?e.__on_r=()=>{s(),r(!0)}:e.__on_r=()=>r(!0),On()}function Nt(e){d===null&&(v===null&&Qt(),Jt()),ae&&Zt()}function kn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function j(e,t,n){var r=d;r!==null&&(r.f&M)!==0&&(e|=M);var s={ctx:T,deps:null,nodes:null,f:e|A|O,first:null,fn:t,last:null,next:null,parent:r,b:r&&r.b,prev:null,teardown:null,wv:0,ac:null};if(n)try{ue(s)}catch(f){throw ie(s),f}else t!==null&&Y(s);var i=s;if(n&&i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&(i.f&Ee)===0&&(i=i.first,(e&q)!==0&&(e&Re)!==0&&i!==null&&(i.f|=Re)),i!==null&&(i.parent=r,r!==null&&kn(i,r),v!==null&&(v.f&g)!==0&&(e&fe)===0)){var l=v;(l.effects??=[]).push(i)}return s}function Ot(){return v!==null&&!P}function Dn(e){const t=j(me,null,!1);return w(t,m),t.teardown=e,t}function Dr(e){Nt();var t=d.f,n=!v&&(t&L)!==0&&(t&z)===0;if(n){var r=T;(r.e??=[]).push(e)}else return kt(e)}function kt(e){return j(we|ct,e,!1)}function Ir(e){return Nt(),j(me|ct,e,!0)}function Pr(e){ce.ensure();const t=j(fe|Ee,e,!0);return(n={})=>new Promise(r=>{n.outro?Mn(t,()=>{ie(t),r(void 0)}):(ie(t),r(void 0))})}function Cr(e){return j(we,e,!1)}function In(e){return j(Ye|Ee,e,!0)}function Mr(e,t=0){return j(me|t,e,!0)}function Fr(e,t=[],n=[],r=[]){mn(r,t,n,s=>{j(me,()=>e(...s.map(ve)),!0)})}function Lr(e,t=0){var n=j(q|t,e,!0);return n}function jr(e){return j(L|Ee,e,!0)}function Dt(e){var t=e.teardown;if(t!==null){const n=ae,r=v;it(!0),K(null);try{t.call(null)}finally{it(n),K(r)}}}function Xe(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){const s=n.ac;s!==null&&Ke(()=>{s.abort(W)});var r=n.next;(n.f&fe)!==0?n.parent=null:ie(n,t),n=r}}function Pn(e){for(var t=e.first;t!==null;){var n=t.next;(t.f&L)===0&&ie(t),t=n}}function ie(e,t=!0){var n=!1;(t||(e.f&ut)!==0)&&e.nodes!==null&&e.nodes.end!==null&&(Cn(e.nodes.start,e.nodes.end),n=!0),Xe(e,t&&!n),ye(e,0),w(e,U);var r=e.nodes&&e.nodes.t;if(r!==null)for(const i of r)i.stop();Dt(e);var s=e.parent;s!==null&&s.first!==null&&It(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=null}function Cn(e,t){for(;e!==null;){var n=e===t?null:X(e);e.remove(),e=n}}function It(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Mn(e,t,n=!0){var r=[];Pt(e,r,!0);var s=()=>{n&&ie(e),t&&t()},i=r.length;if(i>0){var l=()=>--i||s();for(var f of r)f.out(l)}else s()}function Pt(e,t,n){if((e.f&M)===0){e.f^=M;var r=e.nodes&&e.nodes.t;if(r!==null)for(const f of r)(f.is_global||n)&&t.push(f);for(var s=e.first;s!==null;){var i=s.next,l=(s.f&Re)!==0||(s.f&L)!==0&&(e.f&q)!==0;Pt(s,t,l?n:!1),s=i}}}function Hr(e){Ct(e,!0)}function Ct(e,t){if((e.f&M)!==0){e.f^=M,(e.f&m)===0&&(w(e,A),Y(e));for(var n=e.first;n!==null;){var r=n.next,s=(n.f&Re)!==0||(n.f&L)!==0;Ct(n,s?t:!1),n=r}var i=e.nodes&&e.nodes.t;if(i!==null)for(const l of i)(l.is_global||t)&&l.in()}}function Yr(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var s=n===r?null:X(n);t.append(n),n=s}}let Se=!1,ae=!1;function it(e){ae=e}let v=null,P=!1;function K(e){v=e}let d=null;function oe(e){d=e}let k=null;function Mt(e){v!==null&&(k===null?k=[e]:k.push(e))}let R=null,x=0,N=null;function Fn(e){N=e}let Ft=1,J=0,ee=J;function at(e){ee=e}function Lt(){return++Ft}function Te(e){var t=e.f;if((t&A)!==0)return!0;if(t&g&&(e.f&=~ne),(t&F)!==0){for(var n=e.deps,r=n.length,s=0;se.wv)return!0}(t&O)!==0&&I===null&&w(e,m)}return!1}function jt(e,t,n=!0){var r=e.reactions;if(r!==null&&!(k!==null&&le.call(k,e)))for(var s=0;s{e.ac.abort(W)}),e.ac=null);try{e.f|=Ce;var c=e.fn,u=c();e.f|=z;var _=e.deps,b=p?.is_fork;if(R!==null){var h;if(b||ye(e,x),_!==null&&x>0)for(_.length=x+R.length,h=0;he});function Hn(e){return jn?.createHTML(e)??e}function Yn(e){var t=Nn("template");return t.innerHTML=Hn(e.replaceAll("","")),t.content}function te(e,t){var n=d;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function Gr(e,t){var n=(t&sn)!==0,r=(t&an)!==0,s,i=!e.startsWith("");return()=>{if(C)return te(y,null),y;s===void 0&&(s=Yn(i?e:""+e),n||(s=pe(s)));var l=r||St?document.importNode(s,!0):s.cloneNode(!0);if(n){var f=pe(l),a=l.lastChild;te(f,a)}else te(l,l);return l}}function zr(e=""){if(!C){var t=se(e+"");return te(t,t),t}var n=y;return n.nodeType!==ge?(n.before(n=se()),re(n)):De(n),te(n,n),n}function Kr(){if(C)return te(y,null),y;var e=document.createDocumentFragment(),t=document.createComment(""),n=se();return e.append(t,n),te(t,n),e}function Xr(e,t){if(C){var n=d;((n.f&z)===0||n.nodes.end===null)&&(n.nodes.end=y),cn();return}e!==null&&e.before(t)}export{br as $,et as A,qr as B,Ur as C,Mr as D,Re as E,C as F,se as G,nr as H,re as I,pe as J,cn as K,yr as L,ln as M,pr as N,vr as O,y as P,_t as Q,on as R,je as S,Gn as T,jr as U,Wn as V,Or as W,Tn as X,qn as Y,er as Z,sr as _,Xr as a,U as a$,ze as a0,tr as a1,Hr as a2,Mn as a3,M as a4,Qe as a5,L as a6,rr as a7,Nr as a8,ie as a9,A as aA,Y as aB,F as aC,pn as aD,oe as aE,K as aF,xe as aG,dn as aH,v as aI,Ne as aJ,Ee as aK,Qn as aL,_r as aM,Vn as aN,Ar as aO,fn as aP,qe as aQ,Zn as aR,Pr as aS,te as aT,Ve as aU,Q as aV,de as aW,Jn as aX,fr as aY,_e as aZ,ae as a_,X as aa,Yr as ab,un as ac,ft as ad,Bt as ae,$n as af,On as ag,Kn as ah,T as ai,Ir as aj,Xt as ak,Un as al,Br as am,Be as an,wr as ao,_n as ap,Oe as aq,yn as ar,Vr as as,Ot as at,Pe as au,d as av,$t as aw,ur as ax,ce as ay,w as az,Er as b,lr as b0,ar as b1,ir as b2,or as b3,zn as b4,Kt as b5,Kr as c,Gr as d,Sr as e,Rr as f,zr as g,Lr as h,$ as i,V as j,ve as k,gr as l,Bn as m,hr as n,kr as o,mr as p,Cr as q,dr as r,xr as s,Fr as t,Dr as u,Dn as v,Vt as w,cr as x,Tr as y,p as z}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/CAkKRSSs.js b/.svelte-kit/output/client/_app/immutable/chunks/CAkKRSSs.js new file mode 100644 index 0000000..17691ab --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/CAkKRSSs.js @@ -0,0 +1 @@ +import{o as _,B as b,C as i,D as m,F as y,z as v,A as h}from"./BxEwC6Zc.js";function E(e,l,u=l){var f=new WeakSet;_(e,"input",async r=>{var a=r?e.defaultValue:e.value;if(a=o(e)?t(a):a,u(a),v!==null&&f.add(v),await b(),a!==(a=l())){var d=e.selectionStart,s=e.selectionEnd,n=e.value.length;if(e.value=a??"",s!==null){var c=e.value.length;d===s&&s===n&&c>n?(e.selectionStart=c,e.selectionEnd=c):(e.selectionStart=d,e.selectionEnd=Math.min(s,c))}}}),(y&&e.defaultValue!==e.value||i(l)==null&&e.value)&&(u(o(e)?t(e.value):e.value),v!==null&&f.add(v)),m(()=>{var r=l();if(e===document.activeElement){var a=h??v;if(f.has(a))return}o(e)&&r===t(e.value)||e.type==="date"&&!r&&!e.value||r!==e.value&&(e.value=r??"")})}function o(e){var l=e.type;return l==="number"||l==="range"}function t(e){return e===""?null:+e}export{E as b}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/CvGSsBh3.js b/.svelte-kit/output/client/_app/immutable/chunks/CvGSsBh3.js new file mode 100644 index 0000000..4c73729 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/CvGSsBh3.js @@ -0,0 +1 @@ +import{u as a,ai as o,aq as s,C as n,B as l,ar as u,as as c}from"./BxEwC6Zc.js";import{h as r,m as i,u as f}from"./DoLu9QLy.js";function m(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function _(e){o===null&&m(),s&&o.l!==null?p(o).m.push(e):a(()=>{const t=n(e);if(typeof t=="function")return t})}function p(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}const h=Object.freeze(Object.defineProperty({__proto__:null,flushSync:u,hydrate:r,mount:i,onMount:_,settled:c,tick:l,unmount:f,untrack:n},Symbol.toStringTag,{value:"Module"}));export{_ as o,h as s}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/Cy2QBcXt.js b/.svelte-kit/output/client/_app/immutable/chunks/Cy2QBcXt.js new file mode 100644 index 0000000..4b54476 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/Cy2QBcXt.js @@ -0,0 +1 @@ +import{o as _,q as o,v as c,w as b,x as m,y as h,z as v,A as y}from"./BxEwC6Zc.js";function d(e,r,f=!1){if(e.multiple){if(r==null)return;if(!b(r))return m();for(var a of e.options)a.selected=r.includes(i(a));return}for(a of e.options){var t=i(a);if(h(t,r)){a.selected=!0;return}}(!f||r!==void 0)&&(e.selectedIndex=-1)}function q(e){var r=new MutationObserver(()=>{d(e,e.__value)});r.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),c(()=>{r.disconnect()})}function p(e,r,f=r){var a=new WeakSet,t=!0;_(e,"change",u=>{var l=u?"[selected]":":checked",n;if(e.multiple)n=[].map.call(e.querySelectorAll(l),i);else{var s=e.querySelector(l)??e.querySelector("option:not([disabled])");n=s&&i(s)}f(n),v!==null&&a.add(v)}),o(()=>{var u=r();if(e===document.activeElement){var l=y??v;if(a.has(l))return}if(d(e,u,t),t&&u===void 0){var n=e.querySelector(":checked");n!==null&&(u=i(n),f(u))}e.__value=u,t=!1}),q(e)}function i(e){return"__value"in e?e.__value:e.value}export{p as b,q as i,d as s}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/CyUoLS3q.js b/.svelte-kit/output/client/_app/immutable/chunks/CyUoLS3q.js new file mode 100644 index 0000000..80818c3 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/CyUoLS3q.js @@ -0,0 +1 @@ +import{h as p,E as t}from"./BxEwC6Zc.js";import{B as c}from"./kALFGdUG.js";function E(r,s,...a){var e=new c(r);p(()=>{const n=s()??null;e.ensure(n,n&&(o=>n(o,...a)))},t)}export{E as s}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/D2SGgx9j.js b/.svelte-kit/output/client/_app/immutable/chunks/D2SGgx9j.js new file mode 100644 index 0000000..ddb898d --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/D2SGgx9j.js @@ -0,0 +1 @@ +import{p as r}from"./DAtZQVjv.js";import{s as t}from"./BkIq08el.js";const e={get data(){return r.data},get error(){return r.error},get status(){return r.status},get url(){return r.url}};t.updated.check;const o=e;export{o as p}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/DAtZQVjv.js b/.svelte-kit/output/client/_app/immutable/chunks/DAtZQVjv.js new file mode 100644 index 0000000..8e3bd4b --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/DAtZQVjv.js @@ -0,0 +1 @@ +import{o as x}from"./CvGSsBh3.js";import{b5 as w,ap as q,j as u,k as f,i as d}from"./BxEwC6Zc.js";const p=[];function E(e,t=w){let r=null;const n=new Set;function s(a){if(q(e,a)&&(e=a,r)){const i=!p.length;for(const c of n)c[1](),p.push(c,e);if(i){for(let c=0;c{n.delete(c),n.size===0&&r&&(r(),r=null)}}return{set:s,update:l,subscribe:o}}new URL("sveltekit-internal://");function Q(e,t){return e==="/"||t==="ignore"?e:t==="never"?e.endsWith("/")?e.slice(0,-1):e:t==="always"&&!e.endsWith("/")?e+"/":e}function Z(e){return e.split("%25").map(decodeURI).join("%25")}function ee(e){for(const t in e)e[t]=decodeURIComponent(e[t]);return e}function te({href:e}){return e.split("#")[0]}function P(...e){let t=5381;for(const r of e)if(typeof r=="string"){let n=r.length;for(;n;)t=t*33^r.charCodeAt(--n)}else if(ArrayBuffer.isView(r)){const n=new Uint8Array(r.buffer,r.byteOffset,r.byteLength);let s=n.length;for(;s;)t=t*33^n[--s]}else throw new TypeError("value must be a string or TypedArray");return(t>>>0).toString(36)}new TextEncoder;const ne=new TextDecoder;function W(e){const t=atob(e),r=new Uint8Array(t.length);for(let n=0;n((e instanceof Request?e.method:t?.method||"GET")!=="GET"&&m.delete(k(e)),V(e,t));const m=new Map;function re(e,t){const r=k(e,t),n=document.querySelector(r);if(n?.textContent){n.remove();let{body:s,...l}=JSON.parse(n.textContent);const o=n.getAttribute("data-ttl");return o&&m.set(r,{body:s,init:l,ttl:1e3*Number(o)}),n.getAttribute("data-b64")!==null&&(s=W(s)),Promise.resolve(new Response(s,l))}return window.fetch(e,t)}function se(e,t,r){if(m.size>0){const n=k(e,r),s=m.get(n);if(s){if(performance.now(){const s=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(n);if(s)return t.push({name:s[1],matcher:s[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const l=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(n);if(l)return t.push({name:l[1],matcher:l[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!n)return;const o=n.split(/\[(.+?)\](?!\])/);return"/"+o.map((i,c)=>{if(c%2){if(i.startsWith("x+"))return v(String.fromCharCode(parseInt(i.slice(2),16)));if(i.startsWith("u+"))return v(String.fromCharCode(...i.slice(2).split("-").map(C=>parseInt(C,16))));const h=z.exec(i),[,_,b,j,L]=h;return t.push({name:j,matcher:L,optional:!!_,rest:!!b,chained:b?c===1&&o[0]==="":!1}),b?"([^]*?)":_?"([^/]*)?":"([^/]+?)"}return v(i)}).join("")}).join("")}/?$`),params:t}}function G(e){return e!==""&&!/^\([^)]+\)$/.test(e)}function T(e){return e.slice(1).split("/").filter(G)}function oe(e,t,r){const n={},s=e.slice(1),l=s.filter(a=>a!==void 0);let o=0;for(let a=0;ah).join("/"),o=0),c===void 0)if(i.rest)c="";else continue;if(!i.matcher||r[i.matcher](c)){n[i.name]=c;const h=t[a+1],_=s[a+1];h&&!h.rest&&h.optional&&_&&i.chained&&(o=0),!h&&!_&&Object.keys(n).length===l.length&&(o=0);continue}if(i.optional&&i.chained){o++;continue}return}if(!o)return n}function v(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}const Y=/\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;function ie(e,t){const r=T(e),n=e!="/"&&e.endsWith("/");return"/"+r.map(s=>s.replace(Y,(l,o,a,i)=>{const c=t[i];if(!c){if(o||a&&c!==void 0)return"";throw new Error(`Missing parameter '${i}' in route ${e}`)}if(c.startsWith("/")||c.endsWith("/"))throw new Error(`Parameter '${i}' in route ${e} cannot start or end with a slash -- this would cause an invalid route like foo//bar`);return c})).filter(Boolean).join("/")+(n?"/":"")}const B=globalThis.__sveltekit_12t8tv8?.base??"",D=globalThis.__sveltekit_12t8tv8?.assets??B??"",H="1772254297287",ce="sveltekit:snapshot",le="sveltekit:scroll",ue="sveltekit:states",fe="sveltekit:pageurl",de="sveltekit:history",he="sveltekit:navigation",A={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},U=location.origin;function pe(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){const r=document.getElementsByTagName("base");t=r.length?r[0].href:document.URL}return new URL(e,t)}function ge(){return{x:pageXOffset,y:pageYOffset}}function g(e,t){return e.getAttribute(`data-sveltekit-${t}`)}const R={...A,"":A.hover};function I(e){let t=e.assignedSlot??e.parentNode;return t?.nodeType===11&&(t=t.host),t}function _e(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=I(e)}}function me(e,t,r){let n;try{if(n=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),r&&n.hash.match(/^#[^/]/)){const a=location.hash.split("#")[1]||"/";n.hash=`#${a}${n.hash}`}}catch{}const s=e instanceof SVGAElement?e.target.baseVal:e.target,l=!n||!!s||K(n,t,r)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),o=n?.origin===U&&e.hasAttribute("download");return{url:n,external:l,target:s,download:o}}function be(e){let t=null,r=null,n=null,s=null,l=null,o=null,a=e;for(;a&&a!==document.documentElement;)n===null&&(n=g(a,"preload-code")),s===null&&(s=g(a,"preload-data")),t===null&&(t=g(a,"keepfocus")),r===null&&(r=g(a,"noscroll")),l===null&&(l=g(a,"reload")),o===null&&(o=g(a,"replacestate")),a=I(a);function i(c){switch(c){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:R[n??"off"],preload_data:R[s??"off"],keepfocus:i(t),noscroll:i(r),reload:i(l),replace_state:i(o)}}function we(e){const t=E(e);let r=!0;function n(){r=!0,t.update(o=>o)}function s(o){r=!1,t.set(o)}function l(o){let a;return t.subscribe(i=>{(a===void 0||r&&i!==a)&&o(a=i)})}return{notify:n,set:s,subscribe:l}}const N={v:()=>{}};function ve(){const{set:e,subscribe:t}=E(!1);let r;async function n(){clearTimeout(r);try{const s=await fetch(`${D}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!s.ok)return!1;const o=(await s.json()).version!==H;return o&&(e(!0),N.v(),clearTimeout(r)),o}catch{return!1}}return{subscribe:t,check:n}}function K(e,t,r){return e.origin!==U||!e.pathname.startsWith(t)?!0:r?e.pathname!==location.pathname:!1}function ye(e){}const O=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...O];const M=new Set([...O]);[...M];let S,$,y;const X=x.toString().includes("$$")||/function \w+\(\) \{\}/.test(x.toString());X?(S={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL("https://example.com")},$={current:null},y={current:!1}):(S=new class{#e=u({});get data(){return f(this.#e)}set data(t){d(this.#e,t)}#t=u(null);get form(){return f(this.#t)}set form(t){d(this.#t,t)}#n=u(null);get error(){return f(this.#n)}set error(t){d(this.#n,t)}#r=u({});get params(){return f(this.#r)}set params(t){d(this.#r,t)}#s=u({id:null});get route(){return f(this.#s)}set route(t){d(this.#s,t)}#a=u({});get state(){return f(this.#a)}set state(t){d(this.#a,t)}#o=u(-1);get status(){return f(this.#o)}set status(t){d(this.#o,t)}#i=u(new URL("https://example.com"));get url(){return f(this.#i)}set url(t){d(this.#i,t)}},$=new class{#e=u(null);get current(){return f(this.#e)}set current(t){d(this.#e,t)}},y=new class{#e=u(!1);get current(){return f(this.#e)}set current(t){d(this.#e,t)}},N.v=()=>y.current=!0);function Ae(e){Object.assign(S,e)}export{we as A,ye as B,de as H,he as N,fe as P,ue as S,ae as a,B as b,ve as c,pe as d,oe as e,_e as f,me as g,be as h,K as i,te as j,ee as k,Q as l,A as m,$ as n,U as o,S as p,Z as q,ie as r,ge as s,ne as t,Ae as u,ce as v,se as w,re as x,le as y,E as z}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/DoLu9QLy.js b/.svelte-kit/output/client/_app/immutable/chunks/DoLu9QLy.js new file mode 100644 index 0000000..176717a --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/DoLu9QLy.js @@ -0,0 +1,2 @@ +import{at as te,k as W,D as re,C as se,au as H,a5 as D,a0 as j,P as g,F as v,av as b,aw as L,h as ie,K as ae,M as ne,ax as V,U as p,G,ay as I,a3 as k,ab as fe,az as $,aA as oe,aB as q,aC as he,aD as le,aE as N,aF as A,aG as z,aH as de,aI as K,ai as Q,S as ce,a9 as Y,I as O,n as ue,N as _e,aJ as w,E as pe,aK as ge,aL as ve,aM as ye,aN as me,aO as x,J as be,Q as X,aP as Ee,aa as Te,aQ as M,O as R,aR as we,a8 as Re,aS as Se,Y as De,p as Ne,aT as Ae,R as Oe,aU as Fe,b as Ie}from"./BxEwC6Zc.js";function ke(t){let e=0,r=j(0),i;return()=>{te()&&(W(r),re(()=>(e===0&&(i=se(()=>t(()=>H(r)))),e+=1,()=>{D(()=>{e-=1,e===0&&(i?.(),i=void 0,H(r))})})))}}var Ye=pe|ge;function xe(t,e,r,i){new Me(t,e,r,i)}class Me{parent;is_pending=!1;transform_error;#t;#g=v?g:null;#i;#h;#e;#a=null;#r=null;#s=null;#n=null;#l=0;#o=0;#d=!1;#c=new Set;#u=new Set;#f=null;#m=ke(()=>(this.#f=j(this.#l),()=>{this.#f=null}));constructor(e,r,i,f){this.#t=e,this.#i=r,this.#h=s=>{var a=b;a.b=this,a.f|=L,i(s)},this.parent=b.b,this.transform_error=f??this.parent?.transform_error??(s=>s),this.#e=ie(()=>{if(v){const s=this.#g;ae();const a=s.data===ne;if(s.data.startsWith(V)){const n=JSON.parse(s.data.slice(V.length));this.#E(n)}else a?this.#T():this.#b()}else this.#v()},Ye),v&&(this.#t=g)}#b(){try{this.#a=p(()=>this.#h(this.#t))}catch(e){this.error(e)}}#E(e){const r=this.#i.failed;r&&(this.#s=p(()=>{r(this.#t,()=>e,()=>()=>{})}))}#T(){const e=this.#i.pending;e&&(this.is_pending=!0,this.#r=p(()=>e(this.#t)),D(()=>{var r=this.#n=document.createDocumentFragment(),i=G();r.append(i),this.#a=this.#p(()=>(I.ensure(),p(()=>this.#h(i)))),this.#o===0&&(this.#t.before(r),this.#n=null,k(this.#r,()=>{this.#r=null}),this.#_())}))}#v(){try{if(this.is_pending=this.has_pending_snippet(),this.#o=0,this.#l=0,this.#a=p(()=>{this.#h(this.#t)}),this.#o>0){var e=this.#n=document.createDocumentFragment();fe(this.#a,e);const r=this.#i.pending;this.#r=p(()=>r(this.#t))}else this.#_()}catch(r){this.error(r)}}#_(){this.is_pending=!1;for(const e of this.#c)$(e,oe),q(e);for(const e of this.#u)$(e,he),q(e);this.#c.clear(),this.#u.clear()}defer_effect(e){le(e,this.#c,this.#u)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#i.pending}#p(e){var r=b,i=K,f=Q;N(this.#e),A(this.#e),z(this.#e.ctx);try{return e()}catch(s){return de(s),null}finally{N(r),A(i),z(f)}}#y(e){if(!this.has_pending_snippet()){this.parent&&this.parent.#y(e);return}this.#o+=e,this.#o===0&&(this.#_(),this.#r&&k(this.#r,()=>{this.#r=null}),this.#n&&(this.#t.before(this.#n),this.#n=null))}update_pending_count(e){this.#y(e),this.#l+=e,!(!this.#f||this.#d)&&(this.#d=!0,D(()=>{this.#d=!1,this.#f&&ce(this.#f,this.#l)}))}get_effect_pending(){return this.#m(),W(this.#f)}error(e){var r=this.#i.onerror;let i=this.#i.failed;if(!r&&!i)throw e;this.#a&&(Y(this.#a),this.#a=null),this.#r&&(Y(this.#r),this.#r=null),this.#s&&(Y(this.#s),this.#s=null),v&&(O(this.#g),ue(),O(_e()));var f=!1,s=!1;const a=()=>{if(f){ye();return}f=!0,s&&ve(),this.#s!==null&&k(this.#s,()=>{this.#s=null}),this.#p(()=>{I.ensure(),this.#v()})},c=n=>{try{s=!0,r?.(n,a),s=!1}catch(o){w(o,this.#e&&this.#e.parent)}i&&(this.#s=this.#p(()=>{I.ensure();try{return p(()=>{var o=b;o.b=this,o.f|=L,i(this.#t,()=>n,()=>a)})}catch(o){return w(o,this.#e.parent),null}}))};D(()=>{var n;try{n=this.transform_error(e)}catch(o){w(o,this.#e&&this.#e.parent);return}n!==null&&typeof n=="object"&&typeof n.then=="function"?n.then(c,o=>w(o,this.#e&&this.#e.parent)):c(n)})}}const Ce=["touchstart","touchmove"];function Pe(t){return Ce.includes(t)}const T=Symbol("events"),Z=new Set,C=new Set;function Le(t,e,r){(e[T]??={})[t]=r}function Ve(t){for(var e=0;e{throw u});throw _}}finally{t[T]=e,delete t.currentTarget,A(y),N(E)}}}function $e(t,e){var r=e==null?"":typeof e=="object"?`${e}`:e;r!==(t.__t??=t.nodeValue)&&(t.__t=r,t.nodeValue=`${r}`)}function Be(t,e){return ee(t,e)}function qe(t,e){x(),e.intro=e.intro??!1;const r=e.target,i=v,f=g;try{for(var s=be(r);s&&(s.nodeType!==X||s.data!==Ee);)s=Te(s);if(!s)throw M;R(!0),O(s);const a=ee(t,{...e,anchor:s});return R(!1),a}catch(a){if(a instanceof Error&&a.message.split(` +`).some(c=>c.startsWith("https://svelte.dev/e/")))throw a;return a!==M&&console.warn("Failed to hydrate: ",a),e.recover===!1&&we(),x(),Re(r),R(!1),Be(t,e)}finally{R(i),O(f)}}const S=new Map;function ee(t,{target:e,anchor:r,props:i={},events:f,context:s,intro:a=!0,transformError:c}){x();var n=void 0,o=Se(()=>{var y=r??e.appendChild(G());xe(y,{pending:()=>{}},l=>{Ne({});var h=Q;if(s&&(h.c=s),f&&(i.$$events=f),v&&Ae(l,null),n=t(l,i)||{},v&&(b.nodes.end=g,g===null||g.nodeType!==X||g.data!==Oe))throw Fe(),M;Ie()},c);var E=new Set,_=l=>{for(var h=0;h{for(var l of E)for(const u of[e,document]){var h=S.get(u),d=h.get(l);--d==0?(u.removeEventListener(l,U),h.delete(l),h.size===0&&S.delete(u)):h.set(l,d)}C.delete(_),y!==r&&y.parentNode?.removeChild(y)}});return P.set(n,o),n}let P=new WeakMap;function ze(t,e){const r=P.get(t);return r?(P.delete(t),r(e)):Promise.resolve()}export{Le as a,Ve as d,qe as h,Be as m,$e as s,ze as u}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/DsnmJJEf.js b/.svelte-kit/output/client/_app/immutable/chunks/DsnmJJEf.js new file mode 100644 index 0000000..ca27dc7 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/DsnmJJEf.js @@ -0,0 +1 @@ +typeof window<"u"&&((window.__svelte??={}).v??=new Set).add("5"); diff --git a/.svelte-kit/output/client/_app/immutable/chunks/TpZZa089.js b/.svelte-kit/output/client/_app/immutable/chunks/TpZZa089.js new file mode 100644 index 0000000..b7981bb --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/TpZZa089.js @@ -0,0 +1 @@ +import{ai as d,aj as g,u as c,C as m,ak as i,al as b,k as p,am as k,an as v,ao as h}from"./BxEwC6Zc.js";function x(n=!1){const a=d,e=a.l.u;if(!e)return;let f=()=>k(a.s);if(n){let t=0,s={};const _=v(()=>{let l=!1;const r=a.s;for(const o in r)r[o]!==s[o]&&(s[o]=r[o],l=!0);return l&&t++,t});f=()=>p(_)}e.b.length&&g(()=>{u(a,f),i(e.b)}),c(()=>{const t=m(()=>e.m.map(b));return()=>{for(const s of t)typeof s=="function"&&s()}}),e.a.length&&c(()=>{u(a,f),i(e.a)})}function u(n,a){if(n.l.s)for(const e of n.l.s)p(e);a()}h();export{x as i}; diff --git a/.svelte-kit/output/client/_app/immutable/chunks/kALFGdUG.js b/.svelte-kit/output/client/_app/immutable/chunks/kALFGdUG.js new file mode 100644 index 0000000..886d5c7 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/chunks/kALFGdUG.js @@ -0,0 +1 @@ +import{z as n,a2 as u,a9 as o,a3 as m,G as d,U as l,F as p,P as _,ab as v,W as b}from"./BxEwC6Zc.js";class w{anchor;#t=new Map;#s=new Map;#e=new Map;#i=new Set;#a=!0;constructor(e,s=!0){this.anchor=e,this.#a=s}#f=()=>{var e=n;if(this.#t.has(e)){var s=this.#t.get(e),t=this.#s.get(s);if(t)u(t),this.#i.delete(s);else{var a=this.#e.get(s);a&&(this.#s.set(s,a.effect),this.#e.delete(s),a.fragment.lastChild.remove(),this.anchor.before(a.fragment),t=a.effect)}for(const[i,f]of this.#t){if(this.#t.delete(i),i===e)break;const h=this.#e.get(f);h&&(o(h.effect),this.#e.delete(f))}for(const[i,f]of this.#s){if(i===s||this.#i.has(i))continue;const h=()=>{if(Array.from(this.#t.values()).includes(i)){var c=document.createDocumentFragment();v(f,c),c.append(d()),this.#e.set(i,{effect:f,fragment:c})}else o(f);this.#i.delete(i),this.#s.delete(i)};this.#a||!t?(this.#i.add(i),m(f,h,!1)):h()}}};#h=e=>{this.#t.delete(e);const s=Array.from(this.#t.values());for(const[t,a]of this.#e)s.includes(t)||(o(a.effect),this.#e.delete(t))};ensure(e,s){var t=n,a=b();if(s&&!this.#s.has(e)&&!this.#e.has(e))if(a){var i=document.createDocumentFragment(),f=d();i.append(f),this.#e.set(e,{effect:l(()=>s(f)),fragment:i})}else this.#s.set(e,l(()=>s(this.anchor)));if(this.#t.set(t,e),a){for(const[h,r]of this.#s)h===e?t.unskip_effect(r):t.skip_effect(r);for(const[h,r]of this.#e)h===e?t.unskip_effect(r.effect):t.skip_effect(r.effect);t.oncommit(this.#f),t.ondiscard(this.#h)}else p&&(this.anchor=_),this.#f()}}export{w as B}; diff --git a/.svelte-kit/output/client/_app/immutable/entry/app.DL20j9gb.js b/.svelte-kit/output/client/_app/immutable/entry/app.DL20j9gb.js new file mode 100644 index 0000000..72ad082 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/entry/app.DL20j9gb.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.9C686m9W.js","../chunks/DsnmJJEf.js","../chunks/BxEwC6Zc.js","../chunks/CyUoLS3q.js","../chunks/kALFGdUG.js","../nodes/1.RyK1bdTz.js","../chunks/TpZZa089.js","../chunks/DoLu9QLy.js","../chunks/BQKoxf2Q.js","../chunks/D2SGgx9j.js","../chunks/DAtZQVjv.js","../chunks/CvGSsBh3.js","../chunks/BkIq08el.js","../nodes/2.BsR2_q_w.js","../chunks/BLQQas77.js","../chunks/BnX5ZnVA.js","../chunks/BqOsL1Vv.js","../chunks/BAocuFgQ.js","../assets/2.Dv2no-8m.css","../nodes/3.CMuy9Bx3.js","../nodes/4.BdC-uDW-.js","../chunks/Cy2QBcXt.js","../nodes/5.CyqZcxti.js","../nodes/6.CEe0czaO.js","../chunks/CAkKRSSs.js","../nodes/7._nuD3LlN.js","../nodes/8.CkbqikZq.js","../nodes/9.BTBc_Eli.js","../nodes/10.BPlEN6_G.js","../nodes/11.DR1Vz7m0.js","../nodes/12.C9UZWAjJ.js","../nodes/13.CyZ4e6yK.js"])))=>i.map(i=>d[i]); +import{F as X,K as ot,h as ut,E as ct,L as _t,N as lt,I as dt,O as Z,aP as ft,P as mt,q as vt,D as ht,C as H,a5 as gt,aV as K,aW as Et,aX as Pt,aY as bt,k as f,aZ as yt,i as N,a_ as Rt,av as Ot,a$ as pt,b0 as At,aq as Tt,b1 as It,b2 as Lt,an as St,X as Dt,b3 as Vt,b4 as J,ar as wt,aN as xt,$ as Nt,p as jt,aj as kt,u as Ct,B as Bt,f as A,s as Mt,a as O,b as qt,j as U,c as I,e as Yt,r as Ut,d as Q,g as Ft,t as Wt,l as V}from"../chunks/BxEwC6Zc.js";import{h as Xt,m as Zt,u as zt,s as Gt}from"../chunks/DoLu9QLy.js";import"../chunks/DsnmJJEf.js";import{o as Ht}from"../chunks/CvGSsBh3.js";import{i as j}from"../chunks/BQKoxf2Q.js";import{B as Kt}from"../chunks/kALFGdUG.js";function w(r,t,a){var u;X&&(u=mt,ot());var s=new Kt(r);ut(()=>{var e=t()??null;if(X){var n=_t(u),i=n===ft,P=e!==null;if(i!==P){var b=lt();dt(b),s.anchor=b,Z(!1),s.ensure(e,e&&(o=>a(o,e))),Z(!0);return}}s.ensure(e,e&&(o=>a(o,e)))},ct)}function z(r,t){return r===t||r?.[K]===t}function x(r={},t,a,u){return vt(()=>{var s,e;return ht(()=>{s=e,e=[],H(()=>{r!==a(...e)&&(t(r,...e),s&&z(a(...s),r)&&t(null,...s))})}),()=>{gt(()=>{e&&z(a(...e),r)&&t(null,...e)})}}),r}let k=!1;function Jt(r){var t=k;try{return k=!1,[r(),k]}finally{k=t}}function C(r,t,a,u){var s=!Tt||(a&It)!==0,e=(a&At)!==0,n=(a&Vt)!==0,i=u,P=!0,b=()=>(P&&(P=!1,i=n?H(u):u),i),o;if(e){var g=K in r||J in r;o=Et(r,t)?.set??(g&&t in r?c=>r[t]=c:void 0)}var m,v=!1;e?[m,v]=Jt(()=>r[t]):m=r[t],m===void 0&&u!==void 0&&(m=b(),o&&(s&&Pt(),o(m)));var d;if(s?d=()=>{var c=r[t];return c===void 0?b():(P=!0,c)}:d=()=>{var c=r[t];return c!==void 0&&(i=void 0),c===void 0?i:c},s&&(a&bt)===0)return d;if(o){var p=r.$$legacy;return(function(c,E){return arguments.length>0?((!s||!E||p||v)&&o(E?d():c),c):d()})}var _=!1,h=((a&Lt)!==0?St:Dt)(()=>(_=!1,d()));e&&f(h);var y=Ot;return(function(c,E){if(arguments.length>0){const R=E?f(h):s&&e?yt(c):c;return N(h,R),_=!0,i!==void 0&&(i=R),c}return Rt&&_||(y.f&pt)!==0?h.v:f(h)})}function Qt(r){return class extends $t{constructor(t){super({component:r,...t})}}}class $t{#e;#t;constructor(t){var a=new Map,u=(e,n)=>{var i=Nt(n,!1,!1);return a.set(e,i),i};const s=new Proxy({...t.props||{},$$events:{}},{get(e,n){return f(a.get(n)??u(n,Reflect.get(e,n)))},has(e,n){return n===J?!0:(f(a.get(n)??u(n,Reflect.get(e,n))),Reflect.has(e,n))},set(e,n,i){return N(a.get(n)??u(n,i),i),Reflect.set(e,n,i)}});this.#t=(t.hydrate?Xt:Zt)(t.component,{target:t.target,anchor:t.anchor,props:s,context:t.context,intro:t.intro??!1,recover:t.recover,transformError:t.transformError}),(!t?.props?.$$host||t.sync===!1)&&wt(),this.#e=s.$$events;for(const e of Object.keys(this.#t))e==="$set"||e==="$destroy"||e==="$on"||xt(this,e,{get(){return this.#t[e]},set(n){this.#t[e]=n},enumerable:!0});this.#t.$set=e=>{Object.assign(s,e)},this.#t.$destroy=()=>{zt(this.#t)}}$set(t){this.#t.$set(t)}$on(t,a){this.#e[t]=this.#e[t]||[];const u=(...s)=>a.call(this,...s);return this.#e[t].push(u),()=>{this.#e[t]=this.#e[t].filter(s=>s!==u)}}$destroy(){this.#t.$destroy()}}const te="modulepreload",ee=function(r,t){return new URL(r,t).href},G={},l=function(t,a,u){let s=Promise.resolve();if(a&&a.length>0){let b=function(o){return Promise.all(o.map(g=>Promise.resolve(g).then(m=>({status:"fulfilled",value:m}),m=>({status:"rejected",reason:m}))))};const n=document.getElementsByTagName("link"),i=document.querySelector("meta[property=csp-nonce]"),P=i?.nonce||i?.getAttribute("nonce");s=b(a.map(o=>{if(o=ee(o,u),o in G)return;G[o]=!0;const g=o.endsWith(".css"),m=g?'[rel="stylesheet"]':"";if(u)for(let d=n.length-1;d>=0;d--){const p=n[d];if(p.href===o&&(!g||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${o}"]${m}`))return;const v=document.createElement("link");if(v.rel=g?"stylesheet":te,g||(v.as="script"),v.crossOrigin="",v.href=o,P&&v.setAttribute("nonce",P),document.head.appendChild(v),g)return new Promise((d,p)=>{v.addEventListener("load",d),v.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${o}`)))})}))}function e(n){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=n,window.dispatchEvent(i),!i.defaultPrevented)throw n}return s.then(n=>{for(const i of n||[])i.status==="rejected"&&e(i.reason);return t().catch(e)})},me={};var re=Q('
'),ae=Q(" ",1);function ne(r,t){jt(t,!0);let a=C(t,"components",23,()=>[]),u=C(t,"data_0",3,null),s=C(t,"data_1",3,null),e=C(t,"data_2",3,null);kt(()=>t.stores.page.set(t.page)),Ct(()=>{t.stores,t.page,t.constructors,a(),t.form,u(),s(),e(),t.stores.page.notify()});let n=U(!1),i=U(!1),P=U(null);Ht(()=>{const _=t.stores.page.subscribe(()=>{f(n)&&(N(i,!0),Bt().then(()=>{N(P,document.title||"untitled page",!0)}))});return N(n,!0),_});const b=V(()=>t.constructors[2]);var o=ae(),g=A(o);{var m=_=>{const h=V(()=>t.constructors[0]);var y=I(),c=A(y);w(c,()=>f(h),(E,R)=>{x(R(E,{get data(){return u()},get form(){return t.form},get params(){return t.page.params},children:(L,ie)=>{var F=I(),tt=A(F);{var et=T=>{const B=V(()=>t.constructors[1]);var S=I(),M=A(S);w(M,()=>f(B),(q,Y)=>{x(Y(q,{get data(){return s()},get form(){return t.form},get params(){return t.page.params},children:(D,oe)=>{var W=I(),at=A(W);w(at,()=>f(b),(nt,st)=>{x(st(nt,{get data(){return e()},get form(){return t.form},get params(){return t.page.params}}),it=>a()[2]=it,()=>a()?.[2])}),O(D,W)},$$slots:{default:!0}}),D=>a()[1]=D,()=>a()?.[1])}),O(T,S)},rt=T=>{const B=V(()=>t.constructors[1]);var S=I(),M=A(S);w(M,()=>f(B),(q,Y)=>{x(Y(q,{get data(){return s()},get form(){return t.form},get params(){return t.page.params}}),D=>a()[1]=D,()=>a()?.[1])}),O(T,S)};j(tt,T=>{t.constructors[2]?T(et):T(rt,!1)})}O(L,F)},$$slots:{default:!0}}),L=>a()[0]=L,()=>a()?.[0])}),O(_,y)},v=_=>{const h=V(()=>t.constructors[0]);var y=I(),c=A(y);w(c,()=>f(h),(E,R)=>{x(R(E,{get data(){return u()},get form(){return t.form},get params(){return t.page.params}}),L=>a()[0]=L,()=>a()?.[0])}),O(_,y)};j(g,_=>{t.constructors[1]?_(m):_(v,!1)})}var d=Mt(g,2);{var p=_=>{var h=re(),y=Yt(h);{var c=E=>{var R=Ft();Wt(()=>Gt(R,f(P))),O(E,R)};j(y,E=>{f(i)&&E(c)})}Ut(h),O(_,h)};j(d,_=>{f(n)&&_(p)})}O(r,o),qt()}const ve=Qt(ne),he=[()=>l(()=>import("../nodes/0.9C686m9W.js"),__vite__mapDeps([0,1,2,3,4]),import.meta.url),()=>l(()=>import("../nodes/1.RyK1bdTz.js"),__vite__mapDeps([5,1,6,2,7,8,4,9,10,11,12]),import.meta.url),()=>l(()=>import("../nodes/2.BsR2_q_w.js"),__vite__mapDeps([13,1,2,3,4,8,7,14,15,16,9,10,11,12,17,18]),import.meta.url),()=>l(()=>import("../nodes/3.CMuy9Bx3.js"),__vite__mapDeps([19,1,2,7,8,4,14,15,16]),import.meta.url),()=>l(()=>import("../nodes/4.BdC-uDW-.js"),__vite__mapDeps([20,1,2,7,8,4,14,17,12,11,10,15,16,21]),import.meta.url),()=>l(()=>import("../nodes/5.CyqZcxti.js"),__vite__mapDeps([22,1,2,7,8,4,14,17,12,11,10,15]),import.meta.url),()=>l(()=>import("../nodes/6.CEe0czaO.js"),__vite__mapDeps([23,1,2,7,8,4,14,17,12,11,10,15,16,24,21]),import.meta.url),()=>l(()=>import("../nodes/7._nuD3LlN.js"),__vite__mapDeps([25,1,2,7,8,4,14,17,12,11,10,15,16]),import.meta.url),()=>l(()=>import("../nodes/8.CkbqikZq.js"),__vite__mapDeps([26,1,2,7,8,4,14,17,12,11,10,15,16]),import.meta.url),()=>l(()=>import("../nodes/9.BTBc_Eli.js"),__vite__mapDeps([27,1,2,7,8,4,17,12,11,10,15,16]),import.meta.url),()=>l(()=>import("../nodes/10.BPlEN6_G.js"),__vite__mapDeps([28,1,2,7,8,4,14,17,12,11,10,15,16,24]),import.meta.url),()=>l(()=>import("../nodes/11.DR1Vz7m0.js"),__vite__mapDeps([29,1,6,2,15,10,11,7]),import.meta.url),()=>l(()=>import("../nodes/12.C9UZWAjJ.js"),__vite__mapDeps([30,1,2,7,17,12,11,10]),import.meta.url),()=>l(()=>import("../nodes/13.CyZ4e6yK.js"),__vite__mapDeps([31,1,2,7,17,12,11,10]),import.meta.url),()=>l(()=>import("../chunks/BQKoxf2Q.js").then(r=>r._),__vite__mapDeps([8,2,4]),import.meta.url)],ge=[2],Ee={"/(app)":[-4,[2]],"/(app)/admin":[-5,[2]],"/demo":[11],"/demo/better-auth":[-13],"/demo/better-auth/login":[-14],"/login":[-15],"/(app)/mappools":[-6,[2]],"/(app)/mappools/[id]":[-7,[2]],"/(app)/matches":[-8,[2]],"/(app)/matches/[id]":[-9,[2]],"/(app)/settings":[-10,[2]],"/(app)/teams":[-11,[2]]},$={handleError:(({error:r})=>{console.error(r)}),reroute:(()=>{}),transport:{}},se=Object.fromEntries(Object.entries($.transport).map(([r,t])=>[r,t.decode])),Pe=Object.fromEntries(Object.entries($.transport).map(([r,t])=>[r,t.encode])),be=!1,ye=(r,t)=>se[r](t);export{ye as decode,se as decoders,Ee as dictionary,Pe as encoders,be as hash,$ as hooks,me as matchers,he as nodes,ve as root,ge as server_loads}; diff --git a/.svelte-kit/output/client/_app/immutable/entry/start.Df1GVxyt.js b/.svelte-kit/output/client/_app/immutable/entry/start.Df1GVxyt.js new file mode 100644 index 0000000..93acd60 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/entry/start.Df1GVxyt.js @@ -0,0 +1 @@ +import{c as s}from"../chunks/BkIq08el.js";import{B as t}from"../chunks/DAtZQVjv.js";export{t as load_css,s as start}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/0.9C686m9W.js b/.svelte-kit/output/client/_app/immutable/nodes/0.9C686m9W.js new file mode 100644 index 0000000..0e2b0f5 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/0.9C686m9W.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{c as n,f as m,a as p}from"../chunks/BxEwC6Zc.js";import{s as e}from"../chunks/CyUoLS3q.js";function f(a,r){var o=n(),t=m(o);e(t,()=>r.children),p(a,o)}export{f as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/1.RyK1bdTz.js b/.svelte-kit/output/client/_app/immutable/nodes/1.RyK1bdTz.js new file mode 100644 index 0000000..f7b0d81 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/1.RyK1bdTz.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{i as w}from"../chunks/TpZZa089.js";import{p as A,t as j,a,b as B,d as D,e as s,r,s as x,n as N,g as i}from"../chunks/BxEwC6Zc.js";import{s as f}from"../chunks/DoLu9QLy.js";import{i as P}from"../chunks/BQKoxf2Q.js";import{p as o}from"../chunks/D2SGgx9j.js";var S=D('');function H(l,d){A(d,!1),w();var n=S(),m=s(n),c=s(m),u=s(c,!0);r(c);var p=x(c,2),h=s(p);{var g=t=>{var e=i("Page not found");a(t,e)},_=t=>{var e=i("Access denied");a(t,e)},b=t=>{var e=i("Not logged in");a(t,e)},y=t=>{var e=i("Something went wrong");a(t,e)};P(h,t=>{o.status===404?t(g):o.status===403?t(_,1):o.status===401?t(b,2):t(y,!1)})}r(p);var v=x(p,2),k=s(v,!0);r(v),N(2),r(m),r(n),j(()=>{f(u,o.status),f(k,o.error?.message??"An unexpected error occurred.")}),a(l,n),B()}export{H as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/10.BPlEN6_G.js b/.svelte-kit/output/client/_app/immutable/nodes/10.BPlEN6_G.js new file mode 100644 index 0000000..6e9e279 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/10.BPlEN6_G.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as Ge,t as m,a as d,b as He,i as h,k as e,d as n,j as F,s as o,e as r,r as t,l as G,n as Je,c as Ke,f as Le}from"../chunks/BxEwC6Zc.js";import{d as Qe,a as H,s as b}from"../chunks/DoLu9QLy.js";import{i as l}from"../chunks/BQKoxf2Q.js";import{e as ce,i as le}from"../chunks/BLQQas77.js";import{a as W,e as X}from"../chunks/BAocuFgQ.js";import{s as me,r as Z,a as ue}from"../chunks/BnX5ZnVA.js";import{s as Re}from"../chunks/BqOsL1Vv.js";import{b as Se}from"../chunks/CAkKRSSs.js";import"../chunks/DAtZQVjv.js";var Ve=n('
'),We=n('

'),Xe=n(''),Ze=n('
'),$e=n('solo'),et=n(''),tt=n('
'),at=n('
'),rt=n('
'),st=n('
'),ot=n(''),dt=n('
'),nt=n('
'),it=n('
'),vt=n('

My Teams

'),ct=n(''),lt=n('
'),mt=n('

'),ut=n('

Other Teams

'),xt=n('

No teams yet. Your personal team is created when you sign in.

'),ft=n('

Teams

');function Ut(we,_){Ge(_,!0);let O=F(!1),J=F(null),K=F(null),$=F(""),L=F("");const xe=G(()=>_.data.teams.filter(s=>s.members.some(i=>i.userId===_.data.userId))),fe=G(()=>_.data.teams.filter(s=>!s.members.some(i=>i.userId===_.data.userId)));function ee(s){return s.ownerId===_.data.userId}var te=ft(),ae=r(te),re=r(ae),pe=o(r(re),2),Ie=r(pe);t(pe),t(re);var se=o(re,2),ke=r(se,!0);t(se),t(ae);var be=o(ae,2);{var Te=s=>{var i=Ve();W(i,(u,C)=>X?.(u,C),()=>()=>async({result:u,update:C})=>{u.type==="success"&&u.data?.success&&(h(O,!1),await C())}),d(s,i)};l(be,s=>{e(O)&&s(Te)})}var _e=o(be,2);{var Ue=s=>{var i=We(),u=r(i,!0);t(i),m(()=>b(u,e(L))),d(s,i)};l(_e,s=>{e(L)&&s(Ue)})}var ge=o(_e,2);{var Ce=s=>{var i=vt(),u=o(r(i),2);ce(u,21,()=>e(xe),le,(C,a)=>{var M=it(),j=r(M),Q=r(j);{var oe=v=>{var x=Xe();m(()=>me(x,"src",e(a).avatarUrl)),d(v,x)},R=v=>{var x=Ze(),Y=r(x,!0);t(x),m(V=>b(Y,V),[()=>e(a).name.charAt(0).toUpperCase()]),d(v,x)};l(Q,v=>{e(a).avatarUrl?v(oe):v(R,!1)})}var A=o(Q,2),E=r(A),P=r(E),de=r(P,!0);t(P);var k=o(P,2);{var T=v=>{var x=$e();d(v,x)};l(k,v=>{e(a).isPersonal&&v(T)})}t(E);var S=o(E,2),ne=r(S);t(S),t(A);var qe=o(A,2);t(j);var De=o(j,2);{var Pe=v=>{var x=nt(),Y=r(x);ce(Y,21,()=>e(a).members,le,(y,c)=>{var w=rt(),N=r(w);{var q=g=>{var I=et();m(()=>me(I,"src",e(c).user.image)),d(g,I)},U=g=>{var I=tt();d(g,I)};l(N,g=>{e(c).user?.image?g(q):g(U,!1)})}var f=o(N,2),z=r(f,!0);t(f);var D=o(f,2),ie=r(D,!0);t(D);var p=o(D,2);{var B=g=>{var I=at(),ye=r(I);Z(ye),Je(2),t(I),W(I,(ve,Fe)=>X?.(ve,Fe),()=>()=>async({update:ve})=>{await ve()}),m(()=>ue(ye,e(c).id)),d(g,I)},Be=G(()=>ee(e(a))&&e(c).userId!==_.data.userId);l(p,g=>{e(Be)&&g(B)})}t(w),m(()=>{b(z,e(c).user?.name??"Unknown"),b(ie,e(c).role)}),d(y,w)}),t(Y);var V=o(Y,2);{var Ne=y=>{var c=Ke(),w=Le(c);{var N=U=>{var f=st(),z=r(f);Z(z);var D=o(z,2);Z(D);var ie=o(D,4);t(f),W(f,(p,B)=>X?.(p,B),()=>()=>(h(L,""),async({result:p,update:B})=>{p.type==="success"&&p.data?.success?(h($,""),h(K,null),await B()):p.type==="success"&&p.data?.error&&h(L,p.data.error,!0)})),m(()=>ue(z,e(a).id)),Se(D,()=>e($),p=>h($,p)),H("click",ie,()=>h(K,null)),d(U,f)},q=U=>{var f=ot();H("click",f,()=>h(K,e(a).id,!0)),d(U,f)};l(w,U=>{e(K)===e(a).id?U(N):U(q,!1)})}d(y,c)},Oe=G(()=>ee(e(a))&&!e(a).isPersonal);l(V,y=>{e(Oe)&&y(Ne)})}var Ee=o(V,2);{var Ye=y=>{var c=dt(),w=r(c);Z(w);var N=o(w,2);t(c),W(c,q=>X?.(q)),m(()=>ue(w,e(a).id)),H("click",N,q=>{confirm(`Delete ${e(a).name}?`)||q.preventDefault()}),d(y,c)},ze=G(()=>ee(e(a))&&!e(a).isPersonal);l(Ee,y=>{e(ze)&&y(Ye)})}t(x),d(v,x)};l(De,v=>{e(J)===e(a).id&&v(Pe)})}t(M),m(()=>{b(de,e(a).name),b(ne,`${e(a).members.length??""} member${e(a).members.length!==1?"s":""}`),Re(qe,1,`text-sm text-text-secondary transition-transform ${e(J)===e(a).id?"rotate-90":""}`)}),H("click",j,()=>h(J,e(J)===e(a).id?null:e(a).id,!0)),d(C,M)}),t(u),t(i),d(s,i)};l(ge,s=>{e(xe).length>0&&s(Ce)})}var he=o(ge,2);{var Me=s=>{var i=ut(),u=o(r(i),2);ce(u,21,()=>e(fe),le,(C,a)=>{var M=mt(),j=r(M);{var Q=k=>{var T=ct();m(()=>me(T,"src",e(a).avatarUrl)),d(k,T)},oe=k=>{var T=lt(),S=r(T,!0);t(T),m(ne=>b(S,ne),[()=>e(a).name.charAt(0).toUpperCase()]),d(k,T)};l(j,k=>{e(a).avatarUrl?k(Q):k(oe,!1)})}var R=o(j,2),A=r(R),E=r(A,!0);t(A);var P=o(A,2),de=r(P);t(P),t(R),t(M),m(()=>{b(E,e(a).name),b(de,`${e(a).members.length??""} member${e(a).members.length!==1?"s":""}`)}),d(C,M)}),t(u),t(i),d(s,i)};l(he,s=>{e(fe).length>0&&s(Me)})}var je=o(he,2);{var Ae=s=>{var i=xt();d(s,i)};l(je,s=>{_.data.teams.length===0&&s(Ae)})}t(te),m(()=>{b(Ie,`${_.data.teams.length??""} team${_.data.teams.length!==1?"s":""}`),b(ke,e(O)?"Cancel":"New Team")}),H("click",se,()=>h(O,!e(O))),d(we,te),He()}Qe(["click"]);export{Ut as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/11.DR1Vz7m0.js b/.svelte-kit/output/client/_app/immutable/nodes/11.DR1Vz7m0.js new file mode 100644 index 0000000..67e6c80 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/11.DR1Vz7m0.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{i as o}from"../chunks/TpZZa089.js";import{p,t as s,a as m,b as f,d as i}from"../chunks/BxEwC6Zc.js";import{s as n}from"../chunks/BnX5ZnVA.js";import{b as u,r as b}from"../chunks/DAtZQVjv.js";const h="";function _(...t){return u+h+b(t[0],t[1])}var c=i("better-auth");function j(t,a){p(a,!1),o();var e=c();s(r=>n(e,"href",r),[()=>_("/demo/better-auth")]),m(t,e),f()}export{j as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/12.C9UZWAjJ.js b/.svelte-kit/output/client/_app/immutable/nodes/12.C9UZWAjJ.js new file mode 100644 index 0000000..0276207 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/12.C9UZWAjJ.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as h,f as c,t as b,a as l,b as v,d as g,e as o,r as s,s as i}from"../chunks/BxEwC6Zc.js";import{s as n}from"../chunks/DoLu9QLy.js";import{a as x,e as _}from"../chunks/BAocuFgQ.js";var w=g('

',1);function O(m,t){h(t,!0);var r=w(),a=c(r),p=o(a);s(a);var e=i(a,2),u=o(e);s(e);var f=i(e,2);x(f,d=>_?.(d)),b(()=>{n(p,`Hi, ${t.data.user.name??""}!`),n(u,`Your user ID is ${t.data.user.id??""}.`)}),l(m,r),v()}export{O as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/13.CyZ4e6yK.js b/.svelte-kit/output/client/_app/immutable/nodes/13.CyZ4e6yK.js new file mode 100644 index 0000000..501abea --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/13.CyZ4e6yK.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as b,s as t,f as d,t as p,a as c,b as f,d as g,e as h,r as y}from"../chunks/BxEwC6Zc.js";import{s as w}from"../chunks/DoLu9QLy.js";import{a as n,e as i}from"../chunks/BAocuFgQ.js";var x=g('

Login


',1);function I(u,r){b(r,!0);var a=x(),s=t(d(a),2);n(s,o=>i?.(o));var e=t(s,2),l=h(e,!0);y(e);var m=t(e,4);n(m,o=>i?.(o)),p(()=>w(l,r.form?.message??"")),c(u,a),f()}export{I as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/2.BsR2_q_w.js b/.svelte-kit/output/client/_app/immutable/nodes/2.BsR2_q_w.js new file mode 100644 index 0000000..e79d7eb --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/2.BsR2_q_w.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as F,s as i,e as t,r as a,t as S,k as e,a as v,b as H,l as E,f as L,d as m,c as R}from"../chunks/BxEwC6Zc.js";import{s as I}from"../chunks/CyUoLS3q.js";import{i as A}from"../chunks/BQKoxf2Q.js";import{s as z}from"../chunks/DoLu9QLy.js";import{e as T,i as W}from"../chunks/BLQQas77.js";import{s as B}from"../chunks/BnX5ZnVA.js";import{s as M}from"../chunks/BqOsL1Vv.js";import{p as V}from"../chunks/D2SGgx9j.js";import{a as q,e as C}from"../chunks/BAocuFgQ.js";var G=m(' '),J=m('

Staff

⚡ Admin',1),K=m('');function O(j,_){F(_,!0);const d=E(()=>V.data?.user),k=[{href:"/",label:"Dashboard",icon:"⌂"},{href:"/matches",label:"Matches",icon:"⚔"},{href:"/mappools",label:"Mappools",icon:"♫"},{href:"/teams",label:"Teams",icon:"⚑"}];function p(r){return r==="/"?V.url.pathname==="/":V.url.pathname.startsWith(r)}const y=E(()=>e(d)?.role==="admin");var h=K(),o=i(t(h),2),n=t(o);T(n,17,()=>k,W,(r,f)=>{var b=G(),u=t(b),D=t(u,!0);a(u);var $=i(u);a(b),S(l=>{B(b,"href",e(f).href),M(b,1,`font-500 flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors ${l??""}`),z(D,e(f).icon),z($,` ${e(f).label??""}`)},[()=>p(e(f).href)?"bg-accent-dim text-accent":"text-text-secondary hover:bg-surface-700 hover:text-text-primary"]),v(r,b)});var x=i(n,2);{var s=r=>{var f=J(),b=i(L(f),2);S(u=>M(b,1,`font-500 flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors ${u??""}`),[()=>p("/admin")?"bg-red-500/10 text-red-400":"text-text-secondary hover:bg-surface-700 hover:text-text-primary"]),v(r,f)};A(x,r=>{e(y)&&r(s)})}a(o);var c=i(o,2),w=t(c);a(c),a(h),S(r=>M(w,1,`font-500 flex items-center gap-3 rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-700 hover:text-text-primary ${r??""}`),[()=>p("/settings")?"bg-accent-dim text-accent":""]),v(j,h),H()}var P=m(" "),Q=m(''),U=m('
'),X=m('Sign in with osu!'),Y=m('
');function Z(j,_){F(_,!0);const d=E(()=>V.data?.user),k={referee:{label:"REF",color:"bg-yellow-500/20 text-yellow-400"},admin:{label:"ADMIN",color:"bg-red-500/20 text-red-400"}},p=E(()=>e(d)?.role?k[e(d).role]:null);var y=Y(),h=i(t(y),2),o=t(h);{var n=s=>{var c=U(),w=t(c);{var r=l=>{var g=P(),N=t(g,!0);a(g),S(()=>{M(g,1,`rounded px-1.5 py-0.5 text-[10px] font-700 ${e(p).color??""}`),z(N,e(p).label)}),v(l,g)};A(w,l=>{e(p)&&l(r)})}var f=i(w,2);{var b=l=>{var g=Q();S(()=>{B(g,"src",e(d).image),B(g,"alt",e(d).name)}),v(l,g)};A(f,l=>{e(d).image&&l(b)})}var u=i(f,2),D=t(u,!0);a(u);var $=i(u,2);q($,l=>C?.(l)),a(c),S(()=>z(D,e(d).name)),v(s,c)},x=s=>{var c=X();v(s,c)};A(o,s=>{e(d)?s(n):s(x,!1)})}a(h),a(y),v(j,y),H()}var ee=m('
'),te=m('');function fe(j,_){F(_,!0);const d=E(()=>V.data?.user);var k=R(),p=L(k);{var y=o=>{var n=ee(),x=t(n);O(x,{});var s=i(x,2),c=t(s);Z(c,{});var w=i(c,2),r=t(w);I(r,()=>_.children),a(w),a(s),a(n),v(o,n)},h=o=>{var n=te(),x=i(t(n),2),s=t(x);I(s,()=>_.children),a(x),a(n),v(o,n)};A(p,o=>{e(d)?o(y):o(h,!1)})}v(j,k),H()}export{fe as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/3.CMuy9Bx3.js b/.svelte-kit/output/client/_app/immutable/nodes/3.CMuy9Bx3.js new file mode 100644 index 0000000..2ffae4c --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/3.CMuy9Bx3.js @@ -0,0 +1,3 @@ +import"../chunks/DsnmJJEf.js";import{p as Fe,u as Oe,c as Re,f as be,a as o,b as Ue,i as O,j as ge,e as a,s as r,r as t,n as R,k as e,t as m,l as k,d as i}from"../chunks/BxEwC6Zc.js";import{d as Be,s as n,a as he}from"../chunks/DoLu9QLy.js";import{i as L}from"../chunks/BQKoxf2Q.js";import{e as _e,i as ye}from"../chunks/BLQQas77.js";import{s as z}from"../chunks/BnX5ZnVA.js";import{s as U}from"../chunks/BqOsL1Vv.js";var Pe=i(`
V

Welcome to Vash Esports

Automated tournament match management for osu! — lobbies, mappools, picks, bans, and + ELO tracking all in one place.

Sign in with osu!

Uses your osu! account. No extra registration needed.

`),Ge=i(''),He=i(''),Ve=i(""),We=i('
vs
'),Ye=i('

Live Now

'),ze=i('
'),Je=i(''),Ke=i(' - ',1),Xe=i('vs'),Ze=i(' '),$e=i('

'),et=i('

No matches yet. Create one

'),tt=i('

Welcome back,

Automated esports match management for osu!

Total Matches

Completed

Teams

Mappools

Ranked Queue

Find a match at your skill level

');function dt(we,p){Fe(p,!0);const re={CREATED:{label:"Created",color:"text-text-secondary"},LOBBY:{label:"In Lobby",color:"text-yellow-400"},ROLLING:{label:"Rolling",color:"text-yellow-400",dot:"bg-yellow-400"},PICKING:{label:"Picking",color:"text-blue-400",dot:"bg-blue-400"},PLAYING:{label:"Live",color:"text-green-400",dot:"bg-green-400"},FINISHED:{label:"Finished",color:"text-text-secondary"},CANCELLED:{label:"Cancelled",color:"text-red-400"}};let J=ge(null),M=ge(!1);async function B(){try{const c=await fetch("/api/queue");c.ok&&O(J,await c.json(),!0)}catch{}}async function Ie(){O(M,!0);try{const u=await(await fetch("/api/queue",{method:"POST"})).json();if(u.matched&&u.match){window.location.href=`/matches/${u.match.id}`;return}await B()}catch{}O(M,!1)}async function ke(){O(M,!0);try{await fetch("/api/queue",{method:"DELETE"}),await B()}catch{}O(M,!1)}Oe(()=>{if(p.data.user){B();const c=setInterval(B,5e3);return()=>clearInterval(c)}});function Le(c){const u=new Date(c),Q=Date.now()-u.getTime(),j=Math.floor(Q/6e4);if(j<1)return"just now";if(j<60)return`${j}m ago`;const S=Math.floor(j/60);return S<24?`${S}h ago`:`${Math.floor(S/24)}d ago`}var ne=Re(),Me=be(ne);{var je=c=>{var u=Pe();o(c,u)},Ee=c=>{var u=tt(),Q=a(u),j=a(Q),S=r(a(j)),oe=a(S,!0);t(S),t(j),R(2),t(Q);var K=r(Q,2),X=a(K),ce=a(X),Ne=a(ce,!0);t(ce),R(2),t(X);var Z=r(X,2),le=a(Z),Ae=a(le,!0);t(le),R(2),t(Z);var $=r(Z,2),de=a($),Ce=a(de,!0);t(de),R(2),t($);var ie=r($,2),ve=a(ie),Se=a(ve,!0);t(ve),R(2),t(ie),t(K);var xe=r(K,2);{var De=l=>{var s=Ye(),v=r(a(s),2);_e(v,21,()=>p.data.liveMatches,ye,(h,b)=>{const E=k(()=>e(b).participants[0]),g=k(()=>e(b).participants[1]),_=k(()=>re[e(b).state]);var y=We(),N=a(y),P=a(N);{var G=f=>{var I=Ge();m(()=>z(I,"src",e(E).team.avatarUrl)),o(f,I)};L(P,f=>{e(E)?.team.avatarUrl&&f(G)})}var H=r(P,2),te=a(H,!0);t(H),t(N);var D=r(N,2),T=a(D),V=a(T,!0);t(T);var W=r(T,4),Y=a(W,!0);t(W),t(D);var F=r(D,2),A=a(F),ae=a(A,!0);t(A);var d=r(A,2);{var w=f=>{var I=He();m(()=>z(I,"src",e(g).team.avatarUrl)),o(f,I)};L(d,f=>{e(g)?.team.avatarUrl&&f(w)})}t(F);var x=r(F,2),q=a(x);{var C=f=>{var I=Ve();m(()=>U(I,1,`h-1.5 w-1.5 rounded-full ${e(_).dot??""}`)),o(f,I)};L(q,f=>{e(_)?.dot&&f(C)})}var se=r(q);t(x),t(y),m(()=>{z(y,"href",`/matches/${e(b).id??""}`),n(te,e(E)?.team.name??"?"),n(V,e(E)?.score??0),n(Y,e(g)?.score??0),n(ae,e(g)?.team.name??"?"),U(x,1,`flex items-center gap-1.5 text-xs font-500 ${e(_)?.color??""??""}`),n(se,` ${e(_)?.label??""}`)}),o(h,y)}),t(v),t(s),o(l,s)};L(xe,l=>{p.data.liveMatches.length>0&&l(De)})}var ee=r(xe,2),pe=a(ee),qe=r(a(pe),2);{var Qe=l=>{var s=ze(),v=a(s),h=r(a(v));t(v);var b=r(v,2);t(s),m(()=>{n(h,` In queue (${e(J).queueSize??""} searching)`),b.disabled=e(M)}),he("click",b,ke),o(l,s)},Te=l=>{var s=Je(),v=a(s,!0);t(s),m(()=>{s.disabled=e(M),n(v,e(M)?"Joining...":"Find Match")}),he("click",s,Ie),o(l,s)};L(qe,l=>{e(J)?.inQueue?l(Qe):l(Te,!1)})}t(pe),t(ee);var ue=r(ee,2),fe=r(a(ue),2),me=r(a(fe),2);_e(me,21,()=>p.data.recentMatches,ye,(l,s)=>{const v=k(()=>e(s).participants[0]),h=k(()=>e(s).participants[1]),b=k(()=>e(s).config),E=k(()=>re[e(s).state]);var g=$e(),_=a(g),y=a(_),N=a(y),P=a(N,!0);t(N);var G=r(N,2);{var H=d=>{var w=Ke(),x=be(w),q=a(x,!0);t(x);var C=r(x,4),se=a(C,!0);t(C),m(()=>{U(x,1,`text-xs font-700 tabular-nums ${(e(v)?.score??0)>(e(h)?.score??0)?"text-green-400":"text-text-secondary"}`),n(q,e(v)?.score??0),U(C,1,`text-xs font-700 tabular-nums ${(e(h)?.score??0)>(e(v)?.score??0)?"text-green-400":"text-text-secondary"}`),n(se,e(h)?.score??0)}),o(d,w)},te=d=>{var w=Xe();o(d,w)};L(G,d=>{e(s).state==="FINISHED"?d(H):d(te,!1)})}var D=r(G,2),T=a(D,!0);t(D),t(y);var V=r(y,2),W=a(V);t(V),t(_);var Y=r(_,2);{var F=d=>{const w=k(()=>e(s).participants.find(C=>C.teamId===e(s).winnerId));var x=Ze(),q=a(x);t(x),m(()=>n(q,`🏆 ${e(w)?.team.name??""}`)),o(d,x)};L(Y,d=>{e(s).state==="FINISHED"&&e(s).winnerId&&d(F)})}var A=r(Y,2),ae=a(A,!0);t(A),t(g),m(d=>{z(g,"href",`/matches/${e(s).id??""}`),n(P,e(v)?.team.name??"?"),n(T,e(h)?.team.name??"?"),n(W,`${e(s).name?`${e(s).name} · `:""}BO${e(b).bestOf??""} + · ${d??""}`),U(A,1,`text-xs ${e(E)?.color??"text-text-secondary"??""}`),n(ae,e(E)?.label)},[()=>Le(e(s).finishedAt??e(s).createdAt)]),o(l,g)},l=>{var s=et();o(l,s)}),t(me),t(fe),t(ue),t(u),m(()=>{n(oe,p.data.user.name),n(Ne,p.data.stats.matches),n(Ae,p.data.stats.finished),n(Ce,p.data.stats.teams),n(Se,p.data.stats.mappools)}),o(c,u)};L(Me,c=>{p.data.user?c(Ee,!1):c(je)})}o(we,ne),Ue()}Be(["click"]);export{dt as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/4.BdC-uDW-.js b/.svelte-kit/output/client/_app/immutable/nodes/4.BdC-uDW-.js new file mode 100644 index 0000000..52a5797 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/4.BdC-uDW-.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as Z,t as p,a as c,b as $,d as x,e as t,s as o,r as a,k as r,j as ee,i as O,l as te}from"../chunks/BxEwC6Zc.js";import{d as ae,s as i,a as re}from"../chunks/DoLu9QLy.js";import{i as S}from"../chunks/BQKoxf2Q.js";import{e as se,i as oe}from"../chunks/BLQQas77.js";import{a as ne,e as le}from"../chunks/BAocuFgQ.js";import{r as ie,a as de,s as pe}from"../chunks/BnX5ZnVA.js";import{s as ce}from"../chunks/BqOsL1Vv.js";import{i as xe,s as ve}from"../chunks/Cy2QBcXt.js";var me=x('

'),_e=x(''),ue=x('
'),fe=x('

/
'),be=x('

Admin Panel

Avatar User ELO W/L Role
');function je(T,v){Z(v,!0);let m=ee("");const j={player:{label:"Player",color:"bg-surface-600 text-text-secondary border-border"},referee:{label:"Referee",color:"bg-yellow-500/20 text-yellow-400 border-yellow-500/30"},admin:{label:"Admin",color:"bg-red-500/20 text-red-400 border-red-500/30"}};var _=be(),u=t(_),E=o(t(u),2),z=t(E);a(E),a(u);var U=o(u,2);{var D=d=>{var e=me(),f=t(e,!0);a(e),p(()=>i(f,r(m))),c(d,e)};S(U,d=>{r(m)&&d(D)})}var W=o(U,2),G=o(t(W),2);se(G,17,()=>v.data.users,oe,(d,e)=>{const f=te(()=>j[r(e).role]??j.player);var b=fe(),q=t(b);{var H=s=>{var l=_e();p(()=>pe(l,"src",r(e).image)),c(s,l)},J=s=>{var l=ue(),X=t(l,!0);a(l),p(Y=>i(X,Y),[()=>r(e).name.charAt(0).toUpperCase()]),c(s,l)};S(q,s=>{r(e).image?s(H):s(J,!1)})}var g=o(q,2),y=t(g),K=t(y,!0);a(y);var B=o(y,2),M=t(B,!0);a(B),a(g);var h=o(g,2),N=t(h,!0);a(h);var w=o(h,2),A=t(w),Q=t(A);a(A);var C=o(A,2),V=t(C);a(C),a(w);var R=o(w,2),k=t(R);ie(k);var n=o(k,2),L=t(n);L.value=L.__value="player";var P=o(L);P.value=P.__value="referee";var F=o(P);F.value=F.__value="admin",a(n);var I;xe(n),a(R),ne(R,(s,l)=>le?.(s,l),()=>()=>(O(m,""),async({result:s})=>{(s.type==="failure"||s.type==="success"&&s.data?.error)&&O(m,s.data?.error??"Failed to update role",!0)})),a(b),p(()=>{i(K,r(e).name),i(M,r(e).email),i(N,r(e).elo),i(Q,`${r(e).wins??""}W`),i(V,`${r(e).losses??""}L`),de(k,r(e).id),ce(n,1,`rounded-md border px-2 py-1 text-xs font-500 ${r(f).color??""} cursor-pointer bg-transparent focus:outline-none`),I!==(I=r(e).role)&&(n.value=(n.__value=r(e).role)??"",ve(n,r(e).role))}),re("change",n,s=>s.currentTarget.form?.requestSubmit()),c(d,b)}),a(W),a(_),p(()=>i(z,`${v.data.users.length??""} registered user${v.data.users.length!==1?"s":""}`)),c(T,_),$()}ae(["change"]);export{je as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/5.CyqZcxti.js b/.svelte-kit/output/client/_app/immutable/nodes/5.CyqZcxti.js new file mode 100644 index 0000000..57e94eb --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/5.CyqZcxti.js @@ -0,0 +1,2 @@ +import"../chunks/DsnmJJEf.js";import{p as M,t as y,a as d,b as N,k as t,d as i,e as o,s as c,r as a,n as P,i as $,j as q}from"../chunks/BxEwC6Zc.js";import{d as A,a as F,s as v}from"../chunks/DoLu9QLy.js";import{i as G}from"../chunks/BQKoxf2Q.js";import{e as L,i as S}from"../chunks/BLQQas77.js";import{a as z,e as B}from"../chunks/BAocuFgQ.js";import{s as E}from"../chunks/BnX5ZnVA.js";var H=i('
'),I=i('

→
'),J=i('

No mappools yet. Create one to get started.

'),K=i('

Mappools

Create and manage your beatmap pools

');function X(_,f){M(f,!0);let n=q(!1);var l=K(),p=o(l),m=c(o(p),2),k=o(m,!0);a(m),a(p);var b=c(p,2);{var w=r=>{var e=H();z(e,s=>B?.(s)),d(r,e)};G(b,r=>{t(n)&&r(w)})}var u=c(b,2);L(u,21,()=>f.data.mappools,S,(r,e)=>{var s=I(),g=o(s),x=o(g),C=o(x,!0);a(x);var h=c(x,2),j=o(h);a(h),a(g),P(2),a(s),y(D=>{E(s,"href",`/mappools/${t(e).id??""}`),v(C,t(e).name),v(j,`${t(e).slots.length??""} map${t(e).slots.length!==1?"s":""} + · Created ${D??""}`)},[()=>new Date(t(e).createdAt).toLocaleDateString()]),d(r,s)},r=>{var e=J();d(r,e)}),a(u),a(l),y(()=>v(k,t(n)?"Cancel":"New Pool")),F("click",m,()=>$(n,!t(n))),d(_,l),N()}A(["click"]);export{X as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/6.CEe0czaO.js b/.svelte-kit/output/client/_app/immutable/nodes/6.CEe0czaO.js new file mode 100644 index 0000000..008b31b --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/6.CEe0czaO.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as St,t as m,k as r,a as l,b as Tt,e as a,s,d as c,l as st,r as e,i as b,m as Lt,n as Pt,j as M,f as Ut}from"../chunks/BxEwC6Zc.js";import{d as Ct,a as Et,s as i}from"../chunks/DoLu9QLy.js";import{i as q}from"../chunks/BQKoxf2Q.js";import{e as z,i as G}from"../chunks/BLQQas77.js";import{a as J,e as K}from"../chunks/BAocuFgQ.js";import{r as dt,a as Ot,s as nt}from"../chunks/BnX5ZnVA.js";import{s as qt}from"../chunks/BqOsL1Vv.js";import{b as zt}from"../chunks/CAkKRSSs.js";import{b as Gt}from"../chunks/Cy2QBcXt.js";import"../chunks/DAtZQVjv.js";var Jt=c(""),Kt=c('

'),Qt=c(''),Vt=c('
No cover
'),Wt=c('
',1),Xt=c('

'),Yt=c('
'),Zt=c('
'),te=c('

No maps yet. Add your first map above.

'),ee=c('

Add Map

');function me(it,_){St(_,!0);const Q=["NM","HD","HR","DT","FM","TB"];let V=M("NM"),g=M(""),y=M(""),h=M(!1);const pt=st(()=>()=>{const t={};for(const o of Q){const d=_.data.pool.slots.filter(x=>x.category===o);d.length>0&&(t[o]=d)}return t});function vt(t){const o=Math.floor(t/60),d=t%60;return`${o}:${d.toString().padStart(2,"0")}`}const lt={NM:"bg-blue-500/20 text-blue-400 border-blue-500/30",HD:"bg-yellow-500/20 text-yellow-400 border-yellow-500/30",HR:"bg-red-500/20 text-red-400 border-red-500/30",DT:"bg-purple-500/20 text-purple-400 border-purple-500/30",FM:"bg-green-500/20 text-green-400 border-green-500/30",TB:"bg-pink-500/20 text-pink-400 border-pink-500/30"};var j=ee(),B=a(j),I=a(B),W=s(a(I),2),N=a(W),ct=a(N,!0);e(N);var X=s(N,2),mt=a(X);e(X),e(W),e(I);var $=s(I,2),xt=a($);e($),J($,t=>K?.(t)),e(B);var k=s(B,2),A=s(a(k),2),w=a(A);z(w,21,()=>Q,G,(t,o)=>{var d=Jt(),x=a(d,!0);e(d);var f={};m(()=>{i(x,r(o)),f!==(f=r(o))&&(d.value=(d.__value=r(o))??"")}),l(t,d)}),e(w);var F=s(w,2);dt(F);var H=s(F,2),bt=a(H,!0);e(H),e(A);var ft=s(A,2);{var ut=t=>{var o=Kt(),d=a(o,!0);e(o),m(()=>i(d,r(y))),l(t,o)};q(ft,t=>{r(y)&&t(ut)})}e(k),J(k,(t,o)=>K?.(t,o),()=>()=>(b(h,!0),b(y,""),async({result:t,update:o})=>{b(h,!1),t.type==="success"?(b(g,""),await o()):t.type==="failure"&&b(y,t.data?.error??"Failed to add map",!0)}));var Y=s(k,2);z(Y,21,()=>Object.entries(r(pt)()),G,(t,o)=>{var d=st(()=>Lt(r(o),2));let x=()=>r(d)[0],f=()=>r(d)[1];var R=Zt(),S=a(R),D=a(S),_t=a(D,!0);e(D);var Z=s(D,2),gt=a(Z);e(Z),e(S);var tt=s(S,2);z(tt,21,f,G,(yt,p,ht)=>{var T=Yt(),et=a(T);{var kt=n=>{var v=Qt();m(()=>nt(v,"src",r(p).beatmap.coverUrl)),l(n,v)},wt=n=>{var v=Vt();l(n,v)};q(et,n=>{r(p).beatmap?.coverUrl?n(kt):n(wt,!1)})}var L=s(et,2),Dt=a(L);{var Mt=n=>{var v=Wt(),u=Ut(v),It=a(u);e(u);var rt=s(u,2),C=a(rt),Nt=a(C);e(C);var E=s(C,2),$t=a(E);e(E);var O=s(E,2),At=a(O);e(O);var ot=s(O,2),Ft=a(ot,!0);e(ot),e(rt),m((Ht,Rt)=>{nt(u,"href",r(p).beatmap.url),i(It,`${r(p).beatmap.artist??""} - ${r(p).beatmap.title??""}`),i(Nt,`[${r(p).beatmap.version??""}]`),i($t,`★ ${Ht??""}`),i(At,`${r(p).beatmap.bpm??""} BPM`),i(Ft,Rt)},[()=>r(p).beatmap.starRating.toFixed(2),()=>vt(r(p).beatmap.totalLength)]),l(n,v)},jt=n=>{var v=Xt(),u=a(v);e(v),m(()=>i(u,`Beatmap #${r(p).beatmapId??""}`)),l(n,v)};q(Dt,n=>{r(p).beatmap?n(Mt):n(jt,!1)})}e(L);var P=s(L,2),Bt=a(P);e(P);var U=s(P,2),at=a(U);dt(at),Pt(2),e(U),J(U,n=>K?.(n)),e(T),m(()=>{i(Bt,`${x()??""}${ht+1}`),Ot(at,r(p).id)}),l(yt,T)}),e(tt),e(R),m(()=>{qt(D,1,`rounded border px-2 py-0.5 text-xs font-600 ${lt[x()]??"bg-surface-600 text-text-secondary border-border"??""}`),i(_t,x()),i(gt,`${f().length??""} map${f().length!==1?"s":""}`)}),l(t,R)},t=>{var o=te();l(t,o)}),e(Y),e(j),m(t=>{i(ct,_.data.pool.name),i(mt,`${_.data.pool.slots.length??""} map${_.data.pool.slots.length!==1?"s":""}`),H.disabled=t,i(bt,r(h)?"Adding...":"Add")},[()=>r(h)||!r(g).trim()]),Et("click",xt,t=>{confirm("Delete this mappool?")||t.preventDefault()}),Gt(w,()=>r(V),t=>b(V,t)),zt(F,()=>r(g),t=>b(g,t)),l(it,j),Tt()}Ct(["click"]);export{me as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/7._nuD3LlN.js b/.svelte-kit/output/client/_app/immutable/nodes/7._nuD3LlN.js new file mode 100644 index 0000000..025e27c --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/7._nuD3LlN.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as ye,t as h,a as v,b as we,c as Ie,f as W,k as e,i as X,d as f,l as E,s,j as ie,e as a,r as t,n as Me,g as ve}from"../chunks/BxEwC6Zc.js";import{d as Ne,a as Ce,s as p}from"../chunks/DoLu9QLy.js";import{i as N}from"../chunks/BQKoxf2Q.js";import{e as T,i as U}from"../chunks/BLQQas77.js";import{a as Le,e as Oe}from"../chunks/BAocuFgQ.js";import{s as K,b as $e}from"../chunks/BnX5ZnVA.js";import{s as Y}from"../chunks/BqOsL1Vv.js";var Ae=f('· ',1),Se=f('

'),De=f(""),Ee=f(""),Pe=f(""),Be=f(""),ke=f('

Create Match

'),Ge=f(''),je=f(''),Fe=f(""),Re=f('
vs
'),Te=f('

Live Now

'),Ue=f('

Past Matches

'),Ye=f(' - ',1),qe=f('vs'),He=f(' '),Ke=f('

'),ze=f('

No matches yet. Create one to get started.

'),Je=f('

Matches

');function rt(pe,$){ye($,!0);let q=ie(!1),H=ie("");const Z={CREATED:{label:"Created",color:"text-text-secondary border-border"},LOBBY:{label:"In Lobby",color:"text-yellow-400 border-yellow-500/30"},ROLLING:{label:"Rolling",color:"text-yellow-400 border-yellow-500/30",dot:"bg-yellow-400"},PICKING:{label:"Picking",color:"text-blue-400 border-blue-500/30",dot:"bg-blue-400"},PLAYING:{label:"Live",color:"text-green-400 border-green-500/30",dot:"bg-green-400"},FINISHED:{label:"Finished",color:"text-text-secondary border-border"},CANCELLED:{label:"Cancelled",color:"text-red-400 border-red-500/30"}},ee=["LOBBY","ROLLING","PICKING","PLAYING"],j=E(()=>$.data.matches.filter(l=>ee.includes(l.state))),xe=E(()=>$.data.matches.filter(l=>!ee.includes(l.state)));function te(l){const r=l.participants[0],i=l.participants[1];return{p1:r,p2:i}}function ae(l){const r=new Date(l),i=Date.now()-r.getTime(),y=Math.floor(i/6e4);if(y<1)return"just now";if(y<60)return`${y}m ago`;const m=Math.floor(y/60);return m<24?`${m}h ago`:`${Math.floor(m/24)}d ago`}var z=Je(),J=a(z),Q=a(J),re=s(a(Q),2),se=a(re),fe=s(se);{var me=l=>{var r=Ae(),i=s(W(r)),y=a(i);t(i),h(()=>p(y,`${e(j).length??""} live`)),v(l,r)};N(fe,l=>{e(j).length>0&&l(me)})}t(re),t(Q);var V=s(Q,2),ue=a(V,!0);t(V),t(J);var oe=s(J,2);{var _e=l=>{var r=ke(),i=s(a(r),2);{var y=d=>{var o=Se(),n=a(o,!0);t(o),h(()=>p(n,e(H))),v(d,o)};N(i,d=>{e(H)&&d(y)})}var m=s(i,2),x=s(a(m),2),C=s(a(x),2);T(C,20,()=>[3,5,7,9,11,13],U,(d,o)=>{var n=De(),I=a(n);t(n);var u={};h(c=>{$e(n,o===7),p(I,`BO${o??""} (first to ${c??""})`),u!==(u=o)&&(n.value=(n.__value=o)??"")},[()=>Math.ceil(o/2)]),v(d,n)}),t(C),t(x);var M=s(x,2),L=s(a(M),2),A=a(L);A.value=A.__value="";var F=s(A);T(F,17,()=>$.data.teams,U,(d,o)=>{var n=Ee(),I=a(n);t(n);var u={};h(()=>{p(I,`${e(o).name??""}${e(o).isPersonal?" (solo)":""}`),u!==(u=e(o).id)&&(n.value=(n.__value=e(o).id)??"")}),v(d,n)}),t(L),t(M);var S=s(M,2),R=s(a(S),2),O=a(R);O.value=O.__value="";var P=s(O);T(P,17,()=>$.data.teams,U,(d,o)=>{var n=Pe(),I=a(n);t(n);var u={};h(()=>{p(I,`${e(o).name??""}${e(o).isPersonal?" (solo)":""}`),u!==(u=e(o).id)&&(n.value=(n.__value=e(o).id)??"")}),v(d,n)}),t(R),t(S);var B=s(S,2),D=s(a(B),2),k=a(D);k.value=k.__value="";var G=s(k);T(G,17,()=>$.data.mappools,U,(d,o)=>{var n=Be(),I=a(n);t(n);var u={};h(()=>{p(I,`${e(o).name??""} (${e(o).slots.length??""} maps)`),u!==(u=e(o).id)&&(n.value=(n.__value=e(o).id)??"")}),v(d,n)}),t(D),t(B),t(m),Me(2),t(r),Le(r,(d,o)=>Oe?.(d,o),()=>()=>(X(H,""),async({result:d,update:o})=>{d.type==="failure"||d.type==="success"&&d.data?.error?X(H,d.data?.error??"Failed to create match",!0):await o()})),v(l,r)};N(oe,l=>{e(q)&&l(_e)})}var ne=s(oe,2);{var be=l=>{var r=Te(),i=s(a(r),2);T(i,21,()=>e(j),U,(y,m)=>{const x=E(()=>{const{p1:_,p2:g}=te(e(m));return{p1:_,p2:g}}),C=E(()=>Z[e(m).state]);var M=Re(),L=a(M),A=a(L);{var F=_=>{var g=Ge();h(()=>K(g,"src",e(x).p1.team.avatarUrl)),v(_,g)};N(A,_=>{e(x).p1?.team.avatarUrl&&_(F)})}var S=s(A,2),R=a(S,!0);t(S),t(L);var O=s(L,2),P=a(O),B=a(P,!0);t(P);var D=s(P,4),k=a(D,!0);t(D),t(O);var G=s(O,2),d=a(G),o=a(d,!0);t(d);var n=s(d,2);{var I=_=>{var g=je();h(()=>K(g,"src",e(x).p2.team.avatarUrl)),v(_,g)};N(n,_=>{e(x).p2?.team.avatarUrl&&_(I)})}t(G);var u=s(G,2),c=a(u);{var b=_=>{var g=Fe();h(()=>Y(g,1,`h-1.5 w-1.5 rounded-full ${e(C).dot??""}`)),v(_,g)};N(c,_=>{e(C)?.dot&&_(b)})}var w=s(c);t(u),t(M),h(()=>{K(M,"href",`/matches/${e(m).id??""}`),p(R,e(x).p1?.team.name??"?"),p(B,e(x).p1?.score??0),p(k,e(x).p2?.score??0),p(o,e(x).p2?.team.name??"?"),Y(u,1,`flex items-center gap-1.5 rounded border px-2 py-0.5 text-xs font-500 ${e(C)?.color??"border-border"??""}`),p(w,` ${e(C)?.label??e(m).state??""}`)}),v(y,M)}),t(i),t(r),v(l,r)};N(ne,l=>{e(j).length>0&&l(be)})}var le=s(ne,2),de=a(le);{var ge=l=>{var r=Ue();v(l,r)};N(de,l=>{e(j).length>0&&l(ge)})}var ce=s(de,2);T(ce,21,()=>e(xe),U,(l,r)=>{const i=E(()=>{const{p1:c,p2:b}=te(e(r));return{p1:c,p2:b}}),y=E(()=>e(r).config),m=E(()=>Z[e(r).state]);var x=Ke(),C=a(x),M=a(C),L=a(M),A=a(L,!0);t(L);var F=s(L,2);{var S=c=>{var b=Ye(),w=W(b),_=a(w,!0);t(w);var g=s(w,4),he=a(g,!0);t(g),h(()=>{Y(w,1,`text-xs font-700 tabular-nums ${(e(i).p1?.score??0)>(e(i).p2?.score??0)?"text-green-400":"text-text-secondary"}`),p(_,e(i).p1?.score??0),Y(g,1,`text-xs font-700 tabular-nums ${(e(i).p2?.score??0)>(e(i).p1?.score??0)?"text-green-400":"text-text-secondary"}`),p(he,e(i).p2?.score??0)}),v(c,b)},R=c=>{var b=qe();v(c,b)};N(F,c=>{e(r).state==="FINISHED"?c(S):c(R,!1)})}var O=s(F,2),P=a(O,!0);t(O),t(M);var B=s(M,2),D=a(B),k=s(D);{var G=c=>{var b=ve();h(w=>p(b,`· ${w??""}`),[()=>ae(e(r).finishedAt)]),v(c,b)},d=c=>{var b=ve();h(w=>p(b,`· ${w??""}`),[()=>ae(e(r).createdAt)]),v(c,b)};N(k,c=>{e(r).finishedAt?c(G):e(r).createdAt&&c(d,1)})}t(B),t(C);var o=s(C,2);{var n=c=>{const b=E(()=>e(r).participants.find(g=>g.teamId===e(r).winnerId));var w=He(),_=a(w);t(w),h(()=>p(_,`🏆 ${e(b)?.team.name??""}`)),v(c,w)};N(o,c=>{e(r).state==="FINISHED"&&e(r).winnerId&&c(n)})}var I=s(o,2),u=a(I,!0);t(I),t(x),h(()=>{K(x,"href",`/matches/${e(r).id??""}`),p(A,e(i).p1?.team.name??"?"),p(P,e(i).p2?.team.name??"?"),p(D,`${e(r).name?`${e(r).name} · `:""}BO${e(y).bestOf??""} `),Y(I,1,`rounded border px-2 py-0.5 text-xs font-500 ${e(m)?.color??"border-border"??""}`),p(u,e(m)?.label??e(r).state)}),v(l,x)},l=>{var r=Ie(),i=W(r);{var y=m=>{var x=ze();v(m,x)};N(i,m=>{e(j).length===0&&m(y)})}v(l,r)}),t(ce),t(le),t(z),h(()=>{p(se,`${$.data.matches.length??""} match${$.data.matches.length!==1?"es":""} `),p(ue,e(q)?"Cancel":"New Match")}),Ce("click",V,()=>X(q,!e(q))),v(pe,z),we()}Ne(["click"]);export{rt as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/8.CkbqikZq.js b/.svelte-kit/output/client/_app/immutable/nodes/8.CkbqikZq.js new file mode 100644 index 0000000..cc8ee6c --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/8.CkbqikZq.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as xe,u as fe,k as t,t as _,a as d,b as me,d as i,l as g,e as r,s,r as e,f as vt,n as et,i as W,j as ct,m as _e}from"../chunks/BxEwC6Zc.js";import{d as ue,s as v,a as be}from"../chunks/DoLu9QLy.js";import{i as f}from"../chunks/BQKoxf2Q.js";import{e as rt,i as at}from"../chunks/BLQQas77.js";import{a as st,e as ot}from"../chunks/BAocuFgQ.js";import{s as Q,r as ge,a as ye}from"../chunks/BnX5ZnVA.js";import{s as F}from"../chunks/BqOsL1Vv.js";import{i as he}from"../chunks/BkIq08el.js";var Ie=i('· ',1),we=i('
'),Le=i(''),ke=i('

'),Ce=i('

'),$e=i(''),Ne=i('

vs

'),Pe=i('
'),Re=i('

'),Ge=i('

—

'),Se=i('

'),Ee=i('

or type !roll in osu! chat

',1),Oe=i('

Waiting for all rolls...

'),De=i('

🎲 Roll Phase

Highest roll picks first

'),Be=i('

'),je=i(''),Ae=i('
?
'),Fe=i('

',1),Ye=i('

'),He=i('✓'),Te=i('
'),Ue=i('
'),Me=i(`

🎯 Pick a Map

's turn to pick

or type !pick NM1 in osu! chat

`),Ve=i('

',1),Ke=i('
Now Playing

Waiting for players to ready up & play in osu!...

'),We=i('
'),ze=i(''),qe=i(''),Je=i('

'),Qe=i(" "),Xe=i('
-
'),Ze=i('Live'),tr=i(' '),er=i('
'),rr=i('

Games Played

'),ar=i('

Creating osu! lobby...

'),sr=i('

'),or=i('

Lobby created. Waiting for players to join...

'),nr=i('

This match was cancelled.

'),dr=i('');function ur(Dt,q){xe(q,!0);let n=g(()=>q.data.match),D=g(()=>t(n).participants[0]),j=g(()=>t(n).participants[1]);const ht=g(()=>t(n).config),pt=g(()=>Math.ceil(t(ht).bestOf/2)),Bt=g(()=>new Set(t(n).games.map(a=>a.mappoolSlotId))),It=g(()=>[...t(n).participants].sort((a,o)=>(a.pickOrder??99)-(o.pickOrder??99))),jt=g(()=>t(n).games.length%t(It).length),At=g(()=>t(It)[t(jt)]),Ft=g(()=>t(n).state==="ROLLING"&&t(n).participants.some(a=>a.rollValue===null)),xt={NM:"bg-blue-500/20 text-blue-400 border-blue-500/30",HD:"bg-yellow-500/20 text-yellow-400 border-yellow-500/30",HR:"bg-red-500/20 text-red-400 border-red-500/30",DT:"bg-purple-500/20 text-purple-400 border-purple-500/30",FM:"bg-green-500/20 text-green-400 border-green-500/30",TB:"bg-pink-500/20 text-pink-400 border-pink-500/30"},Yt=g(()=>()=>{if(!t(n).mappool?.slots)return{};const a={};for(const o of t(n).mappool.slots)a[o.category]||(a[o.category]=[]),a[o.category].push(o);for(const o of Object.keys(a))a[o].sort((l,c)=>l.orderInCategory-c.orderInCategory);return a});let nt=ct(!1),ft=ct(!1),dt=ct(""),it=ct(!1),mt=null;fe(()=>(["CREATED","LOBBY","ROLLING","PICKING","PLAYING"].includes(t(n).state)&&(mt=setInterval(()=>he(),3e3)),()=>{mt&&clearInterval(mt)}));function Ht(a){const o=Math.floor(a/60),l=a%60;return`${o}:${l.toString().padStart(2,"0")}`}function Tt(a){return{CREATED:"Waiting",LOBBY:"In Lobby",ROLLING:"Rolling",PICKING:"Pick Phase",PLAYING:"Playing",FINISHED:"Finished",CANCELLED:"Cancelled"}[a]??a}function Ut(a){return{ROLLING:"border-yellow-500/30",PICKING:"border-blue-500/30",PLAYING:"border-green-500/30",FINISHED:"border-accent/30"}[a]??"border-border"}var _t=dr(),ut=r(_t),bt=s(r(ut),2),gt=r(bt),Mt=r(gt,!0);e(gt);var wt=s(gt,2),Lt=r(wt),Vt=s(Lt);{var Kt=a=>{var o=Ie(),l=s(vt(o)),c=r(l);e(l),_(()=>{Q(l,"href",`https://osu.ppy.sh/mp/${t(n).osuLobbyId??""}`),v(c,`osu! mp/${t(n).osuLobbyId??""}`)}),d(a,o)};f(Vt,a=>{t(n).osuLobbyId&&a(Kt)})}e(wt),e(bt);var lt=s(bt,2),Wt=r(lt,!0);e(lt);var zt=s(lt,2);{var qt=a=>{var o=we(),l=r(o);e(o),st(o,c=>ot?.(c)),be("click",l,c=>{confirm("Cancel this match?")||c.preventDefault()}),d(a,o)},Jt=g(()=>!["FINISHED","CANCELLED"].includes(t(n).state));f(zt,a=>{t(Jt)&&a(qt)})}e(ut);var kt=s(ut,2);{var Qt=a=>{var o=Ne(),l=r(o),c=r(l);{var p=N=>{var G=Le();_(()=>Q(G,"src",t(D).team.avatarUrl)),d(N,G)};f(c,N=>{t(D).team.avatarUrl&&N(p)})}var h=s(c,2),P=r(h),b=r(P,!0);e(P);var R=s(P,2);{var u=N=>{var G=ke(),V=r(G);e(G),_(()=>v(V,`Roll: ${t(D).rollValue??""}${t(D).pickOrder===1?" ★":""}`)),d(N,G)};f(R,N=>{t(D).rollValue!=null&&N(u)})}e(h);var m=s(h,2),y=r(m,!0);e(m),e(l);var L=s(l,4),k=r(L),B=r(k,!0);e(k);var I=s(k,2),$=r(I),O=r($,!0);e($);var T=s($,2);{var M=N=>{var G=Ce(),V=r(G);e(G),_(()=>v(V,`${t(j).pickOrder===1?"★ ":""}Roll: ${t(j).rollValue??""}`)),d(N,G)};f(T,N=>{t(j).rollValue!=null&&N(M)})}e(I);var Y=s(I,2);{var S=N=>{var G=$e();_(()=>Q(G,"src",t(j).team.avatarUrl)),d(N,G)};f(Y,N=>{t(j).team.avatarUrl&&N(S)})}e(L),e(o),_(()=>{F(l,1,`flex flex-1 items-center gap-4 rounded-lg border p-4 ${t(n).winnerId===t(D).teamId?"border-green-500/40 bg-green-500/5":"border-border bg-surface-800"}`),v(b,t(D).team.name),F(m,1,`text-3xl font-800 tabular-nums ${t(D).score>=t(pt)?"text-green-400":"text-text-primary"}`),v(y,t(D).score),F(L,1,`flex flex-1 items-center gap-4 rounded-lg border p-4 ${t(n).winnerId===t(j).teamId?"border-green-500/40 bg-green-500/5":"border-border bg-surface-800"}`),F(k,1,`text-3xl font-800 tabular-nums ${t(j).score>=t(pt)?"text-green-400":"text-text-primary"}`),v(B,t(j).score),v(O,t(j).team.name)}),d(a,o)};f(kt,a=>{t(D)&&t(j)&&a(Qt)})}var Ct=s(kt,2);{var Xt=a=>{var o=Pe(),l=r(o);st(l,(c,p)=>ot?.(c,p),()=>()=>async({result:c,update:p})=>{c.type==="success"&&c.data?.reinvited&&await p()}),e(o),d(a,o)},Zt=g(()=>["LOBBY","ROLLING","PICKING","PLAYING"].includes(t(n).state));f(Ct,a=>{t(Zt)&&a(Xt)})}var $t=s(Ct,2);{var te=a=>{var o=De(),l=r(o),c=s(r(l),4);rt(c,21,()=>t(n).participants,at,(b,R)=>{var u=Se(),m=r(u),y=r(m,!0);e(m);var L=s(m,2);{var k=I=>{var $=Re(),O=r($,!0);e($),_(()=>v(O,t(R).rollValue)),d(I,$)},B=I=>{var $=Ge();d(I,$)};f(L,I=>{t(R).rollValue!=null?I(k):I(B,!1)})}e(u),_(()=>v(y,t(R).team.name)),d(b,u)}),e(c);var p=s(c,2);{var h=b=>{var R=Ee(),u=vt(R),m=r(u),y=r(m,!0);e(m),e(u),st(u,(L,k)=>ot?.(L,k),()=>()=>(W(nt,!0),async({update:L})=>{W(nt,!1),await L()})),et(2),_(()=>{m.disabled=t(nt),v(y,t(nt)?"Rolling...":"🎲 Roll!")}),d(b,R)},P=b=>{var R=Oe();d(b,R)};f(p,b=>{t(Ft)?b(h):b(P,!1)})}e(l),e(o),d(a,o)};f($t,a=>{t(n).state==="ROLLING"&&a(te)})}var Nt=s($t,2);{var ee=a=>{var o=Me(),l=r(o),c=r(l),p=s(r(c),2),h=r(p),P=r(h,!0);e(h),et(),e(p),et(2),e(c);var b=s(c,2);{var R=m=>{var y=Be(),L=r(y,!0);e(y),_(()=>v(L,t(dt))),d(m,y)};f(b,m=>{t(dt)&&m(R)})}var u=s(b,2);rt(u,21,()=>Object.entries(t(Yt)()),at,(m,y)=>{var L=g(()=>_e(t(y),2));let k=()=>t(L)[0],B=()=>t(L)[1];var I=Ue(),$=r(I),O=r($,!0);e($);var T=s($,2);rt(T,21,B,at,(M,Y)=>{const S=g(()=>q.data.beatmapCache[t(Y).beatmapId]),N=g(()=>t(Bt).has(t(Y).id));var G=Te(),V=r(G);ge(V);var J=s(V,2),x=r(J);{var C=w=>{var E=je();_(()=>Q(E,"src",t(S).listCoverUrl)),d(w,E)},A=w=>{var E=Ae();d(w,E)};f(x,w=>{t(S)?.listCoverUrl?w(C):w(A,!1)})}var U=s(x,2),K=r(U);{var yt=w=>{var E=Fe(),tt=vt(E),ve=r(tt);e(tt);var Ot=s(tt,2),ce=r(Ot);e(Ot),_(pe=>{v(ve,`${t(S).artist??""} - ${t(S).title??""}`),v(ce,`[${t(S).version??""}] · ★${pe??""} · ${t(S).bpm??""}bpm`)},[()=>t(S).starRating.toFixed(1)]),d(w,E)},X=w=>{var E=Ye(),tt=r(E);e(E),_(()=>v(tt,`#${t(Y).beatmapId??""}`)),d(w,E)};f(K,w=>{t(S)?w(yt):w(X,!1)})}e(U);var Z=s(U,2),H=r(Z);e(Z);var z=s(Z,2);{var le=w=>{var E=He();d(w,E)};f(z,w=>{t(N)&&w(le)})}e(J),e(G),st(G,(w,E)=>ot?.(w,E),()=>()=>(W(ft,!0),W(dt,""),async({result:w,update:E})=>{W(ft,!1),w.type==="failure"||w.type==="success"&&w.data?.error?W(dt,w.data?.error??"Pick failed",!0):await E()})),_(()=>{ye(V,t(Y).id),J.disabled=t(N)||t(ft),F(J,1,`flex w-full items-center gap-3 rounded-lg border p-2 text-left transition-all ${t(N)?"border-border/50 bg-surface-900 opacity-30 cursor-not-allowed":"border-border bg-surface-700 hover:border-accent hover:bg-surface-600 cursor-pointer"}`),v(H,`${k()??""}${t(Y).orderInCategory??""}`)}),d(M,G)}),e(T),e(I),_(()=>{F($,1,`mb-2 inline-block rounded border px-2 py-0.5 text-xs font-600 ${xt[k()]??"border-border"??""}`),v(O,k())}),d(m,I)}),e(u),e(l),e(o),_(()=>v(P,t(At)?.team.name)),d(a,o)};f(Nt,a=>{t(n).state==="PICKING"&&a(ee)})}var Pt=s(Nt,2);{var re=a=>{const o=g(()=>t(n).games[t(n).games.length-1]),l=g(()=>t(o)?q.data.beatmapCache[t(o).slot?.beatmapId]:null);var c=Ke(),p=s(r(c),2);{var h=u=>{var m=Ve(),y=vt(m),L=r(y),k=s(L),B=r(k);e(k),e(y);var I=s(y,2),$=r(I);e(I),_((O,T)=>{v(L,`${t(l).artist??""} - ${t(l).title??""} `),v(B,`[${t(l).version??""}]`),v($,`★${O??""} · ${t(l).bpm??""}bpm · ${T??""}`)},[()=>t(l).starRating.toFixed(1),()=>Ht(t(l).totalLength)]),d(u,m)};f(p,u=>{t(l)&&u(h)})}var P=s(p,4),b=r(P),R=r(b,!0);e(b),e(P),st(P,(u,m)=>ot?.(u,m),()=>()=>(W(it,!0),async({update:u})=>{W(it,!1),await u()})),e(c),_(()=>{b.disabled=t(it),v(R,t(it)?"Starting...":"Force Start (skip ready)")}),d(a,c)};f(Pt,a=>{t(n).state==="PLAYING"&&a(re)})}var Rt=s(Pt,2);{var ae=a=>{const o=g(()=>t(n).participants.find(m=>m.teamId===t(n).winnerId));var l=ze(),c=r(l),p=s(r(c),2),h=r(p),P=r(h,!0);e(h),et(),e(p);var b=s(p,2),R=r(b);e(b);var u=s(b,2);rt(u,21,()=>t(n).games,at,(m,y)=>{const L=g(()=>q.data.beatmapCache[t(y).slot?.beatmapId]),k=g(()=>t(n).participants.find(S=>S.id===t(y).winnerParticipantId));var B=We(),I=r(B),$=r(I);e(I);var O=s(I,2),T=r(O,!0);e(O);var M=s(O,2),Y=r(M,!0);e(M),e(B),_(S=>{F(I,1,`font-600 ${S??""}`),v($,`${t(y).slot?.category??""}${t(y).slot?.orderInCategory??""}`),v(T,t(L)?`${t(L).artist} - ${t(L).title}`:`#${t(y).slot?.beatmapId}`),F(M,1,`font-600 ${t(k)?.id===t(D)?.id?"text-blue-400":"text-red-400"}`),v(Y,t(k)?.team.name)},[()=>xt[t(y).slot?.category]?.split(" ")[1]??"text-text-secondary"]),d(m,B)}),e(u),et(2),e(c),e(l),_(()=>{v(P,t(o)?.team.name),v(R,`${t(D)?.score??""} – ${t(j)?.score??""}`)}),d(a,l)};f(Rt,a=>{t(n).state==="FINISHED"&&a(ae)})}var Gt=s(Rt,2);{var se=a=>{var o=rr(),l=s(r(o),2);rt(l,21,()=>t(n).games,at,(c,p)=>{const h=g(()=>q.data.beatmapCache[t(p).slot?.beatmapId]),P=g(()=>t(n).participants.find(x=>x.id===t(p).pickedByParticipantId)?.team),b=g(()=>t(n).participants.find(x=>x.id===t(p).winnerParticipantId)?.team);var R=er(),u=r(R),m=r(u,!0);e(u);var y=s(u,2);{var L=x=>{var C=qe();_(()=>Q(C,"src",t(h).listCoverUrl)),d(x,C)};f(y,x=>{t(h)?.listCoverUrl&&x(L)})}var k=s(y,2),B=r(k);{var I=x=>{var C=Je(),A=r(C),U=s(A),K=r(U);e(U),e(C),_(()=>{v(A,`${t(h).artist??""} - ${t(h).title??""} `),v(K,`[${t(h).version??""}]`)}),d(x,C)};f(B,x=>{t(h)&&x(I)})}var $=s(B,2),O=r($),T=r(O);e(O);var M=s(O,2);{var Y=x=>{var C=Qe(),A=r(C);e(C),_(()=>v(A,`Picked by ${t(P).name??""}`)),d(x,C)};f(M,x=>{t(P)&&x(Y)})}e($),e(k);var S=s(k,2);{var N=x=>{const C=g(()=>t(p).scores.filter(H=>H.player?.participantId===t(D).id).reduce((H,z)=>H+z.score,0)),A=g(()=>t(p).scores.filter(H=>H.player?.participantId===t(j).id).reduce((H,z)=>H+z.score,0));var U=Xe(),K=r(U),yt=r(K,!0);e(K);var X=s(K,4),Z=r(X,!0);e(X),e(U),_((H,z)=>{F(K,1,t(C)>t(A)?"font-700 text-green-400":"text-text-secondary"),v(yt,H),F(X,1,t(A)>t(C)?"font-700 text-green-400":"text-text-secondary"),v(Z,z)},[()=>t(C).toLocaleString(),()=>t(A).toLocaleString()]),d(x,U)},G=x=>{var C=Ze();d(x,C)};f(S,x=>{t(p).state==="FINISHED"&&t(D)&&t(j)?x(N):t(p).state==="PLAYING"&&x(G,1)})}var V=s(S,2);{var J=x=>{var C=tr(),A=r(C);e(C),_(()=>v(A,`✓ ${t(b).name??""}`)),d(x,C)};f(V,x=>{t(b)&&x(J)})}e(R),_(()=>{v(m,t(p).gameNumber),F(O,1,`rounded border px-1.5 py-0.5 ${xt[t(p).slot?.category]??"border-border"??""}`),v(T,`${t(p).slot?.category??""}${t(p).slot?.orderInCategory??""}`)}),d(c,R)}),e(l),e(o),d(a,o)};f(Gt,a=>{t(n).games.length>0&&a(se)})}var St=s(Gt,2);{var oe=a=>{var o=ar();d(a,o)};f(St,a=>{t(n).state==="CREATED"&&a(oe)})}var Et=s(St,2);{var ne=a=>{var o=or(),l=s(r(o),2);{var c=p=>{var h=sr(),P=r(h),b=r(P);e(P),e(h),_(()=>{Q(P,"href",`https://osu.ppy.sh/mp/${t(n).osuLobbyId??""}`),v(b,`osu! mp/${t(n).osuLobbyId??""}`)}),d(p,h)};f(l,p=>{t(n).osuLobbyId&&p(c)})}e(o),d(a,o)};f(Et,a=>{t(n).state==="LOBBY"&&a(ne)})}var de=s(Et,2);{var ie=a=>{var o=nr();d(a,o)};f(de,a=>{t(n).state==="CANCELLED"&&a(ie)})}e(_t),_((a,o)=>{v(Mt,t(n).name||"Match"),v(Lt,`Best of ${t(ht).bestOf??""} · First to ${t(pt)??""} `),F(lt,1,`rounded border px-2.5 py-1 text-xs font-600 ${a??""}`),v(Wt,o)},[()=>Ut(t(n).state),()=>Tt(t(n).state)]),d(Dt,_t),me()}ue(["click"]);export{ur as component}; diff --git a/.svelte-kit/output/client/_app/immutable/nodes/9.BTBc_Eli.js b/.svelte-kit/output/client/_app/immutable/nodes/9.BTBc_Eli.js new file mode 100644 index 0000000..07cdc33 --- /dev/null +++ b/.svelte-kit/output/client/_app/immutable/nodes/9.BTBc_Eli.js @@ -0,0 +1 @@ +import"../chunks/DsnmJJEf.js";import{p as lt,t as l,a as o,b as vt,d as n,s as d,e as t,r as e,n as v,k as c,l as D,g as G}from"../chunks/BxEwC6Zc.js";import{s as i}from"../chunks/DoLu9QLy.js";import{i as k}from"../chunks/BQKoxf2Q.js";import{a as ct,e as xt}from"../chunks/BAocuFgQ.js";import{s as H}from"../chunks/BnX5ZnVA.js";import{s as ft}from"../chunks/BqOsL1Vv.js";var mt=n(''),pt=n('
'),ut=n('Linked'),gt=n('Connect'),_t=n('

Settings

Your profile and account settings

Connected Accounts

🎮

osu!

Ranked Stats

ELO Rating

Wins

Losses

Win Rate

Account

');function At(I,r){lt(r,!0);const L={player:{label:"Player",color:"bg-surface-600 text-text-secondary"},referee:{label:"Referee",color:"bg-yellow-500/20 text-yellow-400"},admin:{label:"Admin",color:"bg-red-500/20 text-red-400"}},R=D(()=>L[r.data.profile.role]??L.player),A=D(()=>r.data.rating.wins+r.data.rating.losses>0?(r.data.rating.wins/(r.data.rating.wins+r.data.rating.losses)*100).toFixed(1):"—");var x=_t(),f=d(t(x),4),m=t(f),C=t(m);{var J=a=>{var s=mt();l(()=>{H(s,"src",r.data.profile.image),H(s,"alt",r.data.profile.name)}),o(a,s)},K=a=>{var s=pt(),ot=t(s,!0);e(s),l(nt=>i(ot,nt),[()=>r.data.profile.name.charAt(0).toUpperCase()]),o(a,s)};k(C,a=>{r.data.profile.image?a(J):a(K,!1)})}var O=d(C,2),p=t(O),u=t(p),M=t(u,!0);e(u);var g=d(u,2),Q=t(g,!0);e(g),e(p);var S=d(p,2),T=t(S,!0);e(S),e(O),e(m);var W=d(m,2),j=d(t(W),2),_=d(t(j),2),B=d(t(_),2),V=t(B);{var X=a=>{var s=G();l(()=>i(s,`Connected as ${r.data.profile.name??""}`)),o(a,s)},Z=a=>{var s=G("Not connected");o(a,s)};k(V,a=>{r.data.hasOsuLinked?a(X):a(Z,!1)})}e(B),e(_);var $=d(_,2);{var tt=a=>{var s=ut();o(a,s)},et=a=>{var s=gt();o(a,s)};k($,a=>{r.data.hasOsuLinked?a(tt):a(et,!1)})}e(j),e(W),e(f);var b=d(f,2),E=d(t(b),2),h=t(E),F=t(h),at=t(F,!0);e(F),v(2),e(h);var y=d(h,2),N=t(y),rt=t(N,!0);e(N),v(2),e(y);var w=d(y,2),P=t(w),st=t(P,!0);e(P),v(2),e(w);var U=d(w,2),Y=t(U),dt=t(Y);e(Y),v(2),e(U),e(E),e(b);var q=d(b,2),z=d(t(q),2),it=t(z);ct(it,a=>xt?.(a)),e(z),e(q),e(x),l(()=>{i(M,r.data.profile.name),ft(g,1,`rounded px-2 py-0.5 text-xs font-600 ${c(R).color??""}`),i(Q,c(R).label),i(T,r.data.profile.email),i(at,r.data.rating.elo),i(rt,r.data.rating.wins),i(st,r.data.rating.losses),i(dt,`${c(A)??""}${c(A)!=="—"?"%":""}`)}),o(I,x),vt()}export{At as component}; diff --git a/.svelte-kit/output/client/_app/version.json b/.svelte-kit/output/client/_app/version.json new file mode 100644 index 0000000..0e8f73c --- /dev/null +++ b/.svelte-kit/output/client/_app/version.json @@ -0,0 +1 @@ +{"version":"1772254297287"} \ No newline at end of file diff --git a/.svelte-kit/output/client/android-chrome-192x192.png b/.svelte-kit/output/client/android-chrome-192x192.png new file mode 100644 index 0000000..e15a9ab Binary files /dev/null and b/.svelte-kit/output/client/android-chrome-192x192.png differ diff --git a/.svelte-kit/output/client/android-chrome-512x512.png b/.svelte-kit/output/client/android-chrome-512x512.png new file mode 100644 index 0000000..58bc4cb Binary files /dev/null and b/.svelte-kit/output/client/android-chrome-512x512.png differ diff --git a/.svelte-kit/output/client/apple-touch-icon.png b/.svelte-kit/output/client/apple-touch-icon.png new file mode 100644 index 0000000..fb8b64b Binary files /dev/null and b/.svelte-kit/output/client/apple-touch-icon.png differ diff --git a/.svelte-kit/output/client/favicon-16x16.png b/.svelte-kit/output/client/favicon-16x16.png new file mode 100644 index 0000000..6700c23 Binary files /dev/null and b/.svelte-kit/output/client/favicon-16x16.png differ diff --git a/.svelte-kit/output/client/favicon-32x32.png b/.svelte-kit/output/client/favicon-32x32.png new file mode 100644 index 0000000..7837088 Binary files /dev/null and b/.svelte-kit/output/client/favicon-32x32.png differ diff --git a/.svelte-kit/output/client/favicon.ico b/.svelte-kit/output/client/favicon.ico new file mode 100644 index 0000000..2c9e68f Binary files /dev/null and b/.svelte-kit/output/client/favicon.ico differ diff --git a/.svelte-kit/output/client/robots.txt b/.svelte-kit/output/client/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/.svelte-kit/output/client/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/.svelte-kit/output/client/site.webmanifest b/.svelte-kit/output/client/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/.svelte-kit/output/client/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/.svelte-kit/output/server/.vite/manifest.json b/.svelte-kit/output/server/.vite/manifest.json new file mode 100644 index 0000000..36de7fb --- /dev/null +++ b/.svelte-kit/output/server/.vite/manifest.json @@ -0,0 +1,661 @@ +{ + ".svelte-kit/generated/server/internal.js": { + "file": "internal.js", + "name": "internal", + "src": ".svelte-kit/generated/server/internal.js", + "isEntry": true, + "imports": [ + "_root.js", + "_environment.js", + "_server.js", + "_internal.js", + "_shared-server.js" + ] + }, + "_api.js": { + "file": "chunks/api.js", + "name": "api", + "imports": [ + "_shared-server.js", + "_index2.js" + ] + }, + "_auth.js": { + "file": "chunks/auth.js", + "name": "auth", + "imports": [ + "_shared-server.js", + "_url.js", + "_root.js", + "_utils.js", + "_query.js", + "_index2.js" + ], + "dynamicImports": [ + "node_modules/better-auth/dist/adapters/memory-adapter/index.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/bun-sqlite-dialect.mjs", + "node_modules/better-auth/dist/adapters/kysely-adapter/node-sqlite-dialect.mjs" + ] + }, + "_engine.js": { + "file": "chunks/engine.js", + "name": "engine", + "imports": [ + "_index2.js" + ] + }, + "_environment.js": { + "file": "chunks/environment.js", + "name": "environment" + }, + "_exports.js": { + "file": "chunks/exports.js", + "name": "exports" + }, + "_false.js": { + "file": "chunks/false.js", + "name": "false" + }, + "_index.js": { + "file": "chunks/index.js", + "name": "index", + "imports": [ + "_index3.js", + "_root.js" + ] + }, + "_index2.js": { + "file": "chunks/index2.js", + "name": "index", + "imports": [ + "_shared-server.js" + ] + }, + "_index3.js": { + "file": "chunks/index3.js", + "name": "index" + }, + "_index4.js": { + "file": "chunks/index4.js", + "name": "index", + "imports": [ + "_state.svelte.js", + "_url.js", + "_utils.js", + "_index.js", + "_root.js", + "_exports.js", + "_index3.js" + ] + }, + "_internal.js": { + "file": "chunks/internal.js", + "name": "internal", + "imports": [ + "_root.js", + "_environment.js", + "_server.js", + "_shared-server.js" + ], + "dynamicImports": [ + "src/hooks.server.ts" + ] + }, + "_orchestrator.js": { + "file": "chunks/orchestrator.js", + "name": "orchestrator", + "imports": [ + "_index2.js", + "_engine.js", + "_shared-server.js" + ] + }, + "_query.js": { + "file": "chunks/query.js", + "name": "query", + "imports": [ + "_shared.js", + "_environment.js" + ] + }, + "_root.js": { + "file": "chunks/root.js", + "name": "root", + "imports": [ + "_index3.js", + "_false.js" + ] + }, + "_routing.js": { + "file": "chunks/routing.js", + "name": "routing", + "imports": [ + "_url.js" + ] + }, + "_server.js": { + "file": "chunks/server.js", + "name": "server" + }, + "_shared-server.js": { + "file": "chunks/shared-server.js", + "name": "shared-server" + }, + "_shared.js": { + "file": "chunks/shared.js", + "name": "shared", + "imports": [ + "_utils.js" + ] + }, + "_state.svelte.js": { + "file": "chunks/state.svelte.js", + "name": "state.svelte", + "imports": [ + "_index3.js", + "_url.js", + "_root.js" + ] + }, + "_url.js": { + "file": "chunks/url.js", + "name": "url" + }, + "_utils.js": { + "file": "chunks/utils.js", + "name": "utils" + }, + "node_modules/@sveltejs/kit/src/runtime/app/server/remote/index.js": { + "file": "remote-entry.js", + "name": "remote-entry", + "src": "node_modules/@sveltejs/kit/src/runtime/app/server/remote/index.js", + "isEntry": true, + "imports": [ + "_query.js", + "_shared.js", + "_false.js", + "_server.js" + ] + }, + "node_modules/@sveltejs/kit/src/runtime/components/svelte-5/layout.svelte": { + "file": "entries/fallbacks/layout.svelte.js", + "name": "entries/fallbacks/layout.svelte", + "src": "node_modules/@sveltejs/kit/src/runtime/components/svelte-5/layout.svelte", + "isEntry": true + }, + "node_modules/@sveltejs/kit/src/runtime/server/index.js": { + "file": "index.js", + "name": "index", + "src": "node_modules/@sveltejs/kit/src/runtime/server/index.js", + "isEntry": true, + "imports": [ + "_false.js", + "_server.js", + "_shared.js", + "_url.js", + "_utils.js", + "_index.js", + "_shared-server.js", + "_routing.js", + "_exports.js", + "_internal.js" + ] + }, + "node_modules/better-auth/dist/adapters/kysely-adapter/bun-sqlite-dialect.mjs": { + "file": "chunks/bun-sqlite-dialect.js", + "name": "bun-sqlite-dialect", + "src": "node_modules/better-auth/dist/adapters/kysely-adapter/bun-sqlite-dialect.mjs", + "isDynamicEntry": true + }, + "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs": { + "file": "chunks/index6.js", + "name": "index", + "src": "node_modules/better-auth/dist/adapters/kysely-adapter/index.mjs", + "isDynamicEntry": true, + "imports": [ + "_auth.js" + ] + }, + "node_modules/better-auth/dist/adapters/kysely-adapter/node-sqlite-dialect.mjs": { + "file": "chunks/node-sqlite-dialect.js", + "name": "node-sqlite-dialect", + "src": "node_modules/better-auth/dist/adapters/kysely-adapter/node-sqlite-dialect.mjs", + "isDynamicEntry": true + }, + "node_modules/better-auth/dist/adapters/memory-adapter/index.mjs": { + "file": "chunks/index5.js", + "name": "index", + "src": "node_modules/better-auth/dist/adapters/memory-adapter/index.mjs", + "isDynamicEntry": true + }, + "src/hooks.server.ts": { + "file": "entries/hooks.server.js", + "name": "entries/hooks.server", + "src": "src/hooks.server.ts", + "isEntry": true, + "isDynamicEntry": true, + "imports": [ + "_environment.js", + "_auth.js", + "_index2.js" + ] + }, + "src/routes/(app)/+layout.server.ts": { + "file": "entries/pages/(app)/_layout.server.ts.js", + "name": "entries/pages/(app)/_layout.server.ts", + "src": "src/routes/(app)/+layout.server.ts", + "isEntry": true + }, + "src/routes/(app)/+layout.svelte": { + "file": "entries/pages/(app)/_layout.svelte.js", + "name": "entries/pages/(app)/_layout.svelte", + "src": "src/routes/(app)/+layout.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_index4.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ], + "css": [ + "_app/immutable/assets/_layout.Dv2no-8m.css" + ] + }, + "src/routes/(app)/+page.server.ts": { + "file": "entries/pages/(app)/_page.server.ts.js", + "name": "entries/pages/(app)/_page.server.ts", + "src": "src/routes/(app)/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/+page.svelte": { + "file": "entries/pages/(app)/_page.svelte.js", + "name": "entries/pages/(app)/_page.svelte", + "src": "src/routes/(app)/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js" + ] + }, + "src/routes/(app)/admin/+page.server.ts": { + "file": "entries/pages/(app)/admin/_page.server.ts.js", + "name": "entries/pages/(app)/admin/_page.server.ts", + "src": "src/routes/(app)/admin/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/admin/+page.svelte": { + "file": "entries/pages/(app)/admin/_page.svelte.js", + "name": "entries/pages/(app)/admin/_page.svelte", + "src": "src/routes/(app)/admin/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/mappools/+page.server.ts": { + "file": "entries/pages/(app)/mappools/_page.server.ts.js", + "name": "entries/pages/(app)/mappools/_page.server.ts", + "src": "src/routes/(app)/mappools/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/mappools/+page.svelte": { + "file": "entries/pages/(app)/mappools/_page.svelte.js", + "name": "entries/pages/(app)/mappools/_page.svelte", + "src": "src/routes/(app)/mappools/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/mappools/[id]/+page.server.ts": { + "file": "entries/pages/(app)/mappools/_id_/_page.server.ts.js", + "name": "entries/pages/(app)/mappools/_id_/_page.server.ts", + "src": "src/routes/(app)/mappools/[id]/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_api.js" + ] + }, + "src/routes/(app)/mappools/[id]/+page.svelte": { + "file": "entries/pages/(app)/mappools/_id_/_page.svelte.js", + "name": "entries/pages/(app)/mappools/_id_/_page.svelte", + "src": "src/routes/(app)/mappools/[id]/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/matches/+page.server.ts": { + "file": "entries/pages/(app)/matches/_page.server.ts.js", + "name": "entries/pages/(app)/matches/_page.server.ts", + "src": "src/routes/(app)/matches/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_engine.js", + "_orchestrator.js" + ] + }, + "src/routes/(app)/matches/+page.svelte": { + "file": "entries/pages/(app)/matches/_page.svelte.js", + "name": "entries/pages/(app)/matches/_page.svelte", + "src": "src/routes/(app)/matches/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/matches/[id]/+page.server.ts": { + "file": "entries/pages/(app)/matches/_id_/_page.server.ts.js", + "name": "entries/pages/(app)/matches/_id_/_page.server.ts", + "src": "src/routes/(app)/matches/[id]/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_engine.js", + "_orchestrator.js", + "_api.js" + ] + }, + "src/routes/(app)/matches/[id]/+page.svelte": { + "file": "entries/pages/(app)/matches/_id_/_page.svelte.js", + "name": "entries/pages/(app)/matches/_id_/_page.svelte", + "src": "src/routes/(app)/matches/[id]/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/settings/+page.server.ts": { + "file": "entries/pages/(app)/settings/_page.server.ts.js", + "name": "entries/pages/(app)/settings/_page.server.ts", + "src": "src/routes/(app)/settings/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js", + "_index2.js" + ] + }, + "src/routes/(app)/settings/+page.svelte": { + "file": "entries/pages/(app)/settings/_page.svelte.js", + "name": "entries/pages/(app)/settings/_page.svelte", + "src": "src/routes/(app)/settings/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/(app)/teams/+page.server.ts": { + "file": "entries/pages/(app)/teams/_page.server.ts.js", + "name": "entries/pages/(app)/teams/_page.server.ts", + "src": "src/routes/(app)/teams/+page.server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/(app)/teams/+page.svelte": { + "file": "entries/pages/(app)/teams/_page.svelte.js", + "name": "entries/pages/(app)/teams/_page.svelte", + "src": "src/routes/(app)/teams/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/+error.svelte": { + "file": "entries/pages/_error.svelte.js", + "name": "entries/pages/_error.svelte", + "src": "src/routes/+error.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_index4.js" + ] + }, + "src/routes/api/health/+server.ts": { + "file": "entries/endpoints/api/health/_server.ts.js", + "name": "entries/endpoints/api/health/_server.ts", + "src": "src/routes/api/health/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/+server.ts": { + "file": "entries/endpoints/api/mappools/_server.ts.js", + "name": "entries/endpoints/api/mappools/_server.ts", + "src": "src/routes/api/mappools/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/[id]/+server.ts": { + "file": "entries/endpoints/api/mappools/_id_/_server.ts.js", + "name": "entries/endpoints/api/mappools/_id_/_server.ts", + "src": "src/routes/api/mappools/[id]/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/[id]/slots/+server.ts": { + "file": "entries/endpoints/api/mappools/_id_/slots/_server.ts.js", + "name": "entries/endpoints/api/mappools/_id_/slots/_server.ts", + "src": "src/routes/api/mappools/[id]/slots/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/mappools/[id]/slots/[slotId]/+server.ts": { + "file": "entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js", + "name": "entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts", + "src": "src/routes/api/mappools/[id]/slots/[slotId]/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js" + ] + }, + "src/routes/api/matches/+server.ts": { + "file": "entries/endpoints/api/matches/_server.ts.js", + "name": "entries/endpoints/api/matches/_server.ts", + "src": "src/routes/api/matches/+server.ts", + "isEntry": true, + "imports": [ + "_index2.js", + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/_server.ts", + "src": "src/routes/api/matches/[id]/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/lobby/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/lobby/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/lobby/_server.ts", + "src": "src/routes/api/matches/[id]/lobby/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/pick/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/pick/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/pick/_server.ts", + "src": "src/routes/api/matches/[id]/pick/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/roll/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/roll/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/roll/_server.ts", + "src": "src/routes/api/matches/[id]/roll/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/rolling/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/rolling/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/rolling/_server.ts", + "src": "src/routes/api/matches/[id]/rolling/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/matches/[id]/score/+server.ts": { + "file": "entries/endpoints/api/matches/_id_/score/_server.ts.js", + "name": "entries/endpoints/api/matches/_id_/score/_server.ts", + "src": "src/routes/api/matches/[id]/score/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js" + ] + }, + "src/routes/api/osu/beatmap/[id]/+server.ts": { + "file": "entries/endpoints/api/osu/beatmap/_id_/_server.ts.js", + "name": "entries/endpoints/api/osu/beatmap/_id_/_server.ts", + "src": "src/routes/api/osu/beatmap/[id]/+server.ts", + "isEntry": true, + "imports": [ + "_api.js" + ] + }, + "src/routes/api/queue/+server.ts": { + "file": "entries/endpoints/api/queue/_server.ts.js", + "name": "entries/endpoints/api/queue/_server.ts", + "src": "src/routes/api/queue/+server.ts", + "isEntry": true, + "imports": [ + "_engine.js", + "_index2.js" + ] + }, + "src/routes/demo/+page.svelte": { + "file": "entries/pages/demo/_page.svelte.js", + "name": "entries/pages/demo/_page.svelte", + "src": "src/routes/demo/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_server.js", + "_routing.js", + "_url.js", + "_root.js" + ] + }, + "src/routes/demo/better-auth/+page.server.ts": { + "file": "entries/pages/demo/better-auth/_page.server.ts.js", + "name": "entries/pages/demo/better-auth/_page.server.ts", + "src": "src/routes/demo/better-auth/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js" + ] + }, + "src/routes/demo/better-auth/+page.svelte": { + "file": "entries/pages/demo/better-auth/_page.svelte.js", + "name": "entries/pages/demo/better-auth/_page.svelte", + "src": "src/routes/demo/better-auth/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/demo/better-auth/login/+page.server.ts": { + "file": "entries/pages/demo/better-auth/login/_page.server.ts.js", + "name": "entries/pages/demo/better-auth/login/_page.server.ts", + "src": "src/routes/demo/better-auth/login/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js" + ] + }, + "src/routes/demo/better-auth/login/+page.svelte": { + "file": "entries/pages/demo/better-auth/login/_page.svelte.js", + "name": "entries/pages/demo/better-auth/login/_page.svelte", + "src": "src/routes/demo/better-auth/login/+page.svelte", + "isEntry": true, + "imports": [ + "_index3.js", + "_url.js", + "_utils.js", + "_root.js", + "_exports.js", + "_state.svelte.js" + ] + }, + "src/routes/login/+page.server.ts": { + "file": "entries/pages/login/_page.server.ts.js", + "name": "entries/pages/login/_page.server.ts", + "src": "src/routes/login/+page.server.ts", + "isEntry": true, + "imports": [ + "_auth.js" + ] + } +} \ No newline at end of file diff --git a/.svelte-kit/output/server/_app/immutable/assets/_layout.Dv2no-8m.css b/.svelte-kit/output/server/_app/immutable/assets/_layout.Dv2no-8m.css new file mode 100644 index 0000000..d32e9ae --- /dev/null +++ b/.svelte-kit/output/server/_app/immutable/assets/_layout.Dv2no-8m.css @@ -0,0 +1 @@ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:"Geist Mono", ui-monospace, monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-500:oklch(65.6% .241 354.308);--color-gray-300:oklch(87.2% .01 258.338);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-6xl:3.75rem;--text-6xl--line-height:1;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--blur-md:12px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface-900:#0a0a0f;--color-surface-800:#12121a;--color-surface-700:#1a1a25;--color-surface-600:#24243a;--color-accent:#ff66ab;--color-accent-hover:#ff85be;--color-accent-dim:#ff66ab33;--color-text-primary:#eeeef0;--color-text-secondary:#8888a0;--color-border:#2a2a3d;--font-body:"Geist", system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.mx-auto{margin-inline:auto}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-56{margin-left:calc(var(--spacing) * 56)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.table{display:table}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-full{height:100%}.max-h-\[80vh\]{max-height:80vh}.min-h-\[70vh\]{min-height:70vh}.min-h-dvh{min-height:100dvh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.rotate-90{rotate:90deg}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[auto_1fr_auto_auto_auto\]{grid-template-columns:auto 1fr auto auto auto}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent\/20{border-color:#ff66ab33}@supports (color:color-mix(in lab,red,red)){.border-accent\/20{border-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.border-accent\/30{border-color:#ff66ab4d}@supports (color:color-mix(in lab,red,red)){.border-accent\/30{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/30{border-color:color-mix(in oklab,var(--color-blue-500) 30%,transparent)}}.border-border{border-color:var(--color-border)}.border-border\/50{border-color:#2a2a3d80}@supports (color:color-mix(in lab,red,red)){.border-border\/50{border-color:color-mix(in oklab,var(--color-border) 50%,transparent)}}.border-gray-300{border-color:var(--color-gray-300)}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.border-green-500\/30{border-color:#00c7584d}@supports (color:color-mix(in lab,red,red)){.border-green-500\/30{border-color:color-mix(in oklab,var(--color-green-500) 30%,transparent)}}.border-green-500\/40{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.border-green-500\/40{border-color:color-mix(in oklab,var(--color-green-500) 40%,transparent)}}.border-pink-500\/30{border-color:#f6339a4d}@supports (color:color-mix(in lab,red,red)){.border-pink-500\/30{border-color:color-mix(in oklab,var(--color-pink-500) 30%,transparent)}}.border-purple-500\/30{border-color:#ac4bff4d}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/30{border-color:color-mix(in oklab,var(--color-purple-500) 30%,transparent)}}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500) 30%,transparent)}}.border-yellow-500\/30{border-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/30{border-color:color-mix(in oklab,var(--color-yellow-500) 30%,transparent)}}.bg-accent{background-color:var(--color-accent)}.bg-accent-dim{background-color:var(--color-accent-dim)}.bg-accent\/10{background-color:#ff66ab1a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500) 10%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.bg-blue-600{background-color:var(--color-blue-600)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500\/5{background-color:#00c7580d}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/5{background-color:color-mix(in oklab,var(--color-green-500) 5%,transparent)}}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/20{background-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.bg-pink-500\/20{background-color:#f6339a33}@supports (color:color-mix(in lab,red,red)){.bg-pink-500\/20{background-color:color-mix(in oklab,var(--color-pink-500) 20%,transparent)}}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500) 10%,transparent)}}.bg-purple-500\/20{background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/20{background-color:color-mix(in oklab,var(--color-purple-500) 20%,transparent)}}.bg-red-500\/5{background-color:#fb2c360d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/5{background-color:color-mix(in oklab,var(--color-red-500) 5%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.bg-surface-600{background-color:var(--color-surface-600)}.bg-surface-700{background-color:var(--color-surface-700)}.bg-surface-800{background-color:var(--color-surface-800)}.bg-surface-900{background-color:var(--color-surface-900)}.bg-surface-900\/80{background-color:#0a0a0fcc}@supports (color:color-mix(in lab,red,red)){.bg-surface-900\/80{background-color:color-mix(in oklab,var(--color-surface-900) 80%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-yellow-500\/5{background-color:#edb2000d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/5{background-color:color-mix(in oklab,var(--color-yellow-500) 5%,transparent)}}.bg-yellow-500\/20{background-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/20{background-color:color-mix(in oklab,var(--color-yellow-500) 20%,transparent)}}.object-cover{object-fit:cover}.p-1{padding:calc(var(--spacing) * 1)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-accent{color:var(--color-accent)}.text-blue-400{color:var(--color-blue-400)}.text-green-400{color:var(--color-green-400)}.text-pink-400{color:var(--color-pink-400)}.text-purple-400{color:var(--color-purple-400)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-surface-900{color:var(--color-surface-900)}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-text-secondary\/30{color:#8888a04d}@supports (color:color-mix(in lab,red,red)){.text-text-secondary\/30{color:color-mix(in oklab,var(--color-text-secondary) 30%,transparent)}}.text-white{color:var(--color-white)}.text-yellow-400{color:var(--color-yellow-400)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.group-hover\:text-accent:is(:where(.group):hover *){color:var(--color-accent)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-text-secondary\/50::placeholder{color:#8888a080}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-text-secondary\/50::placeholder{color:color-mix(in oklab,var(--color-text-secondary) 50%,transparent)}}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:border-accent:hover{border-color:var(--color-accent)}.hover\:border-accent\/30:hover{border-color:#ff66ab4d}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/30:hover{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.hover\:border-accent\/40:hover{border-color:#ff66ab66}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/40:hover{border-color:color-mix(in oklab,var(--color-accent) 40%,transparent)}}.hover\:border-green-500\/40:hover{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.hover\:border-green-500\/40:hover{border-color:color-mix(in oklab,var(--color-green-500) 40%,transparent)}}.hover\:border-red-500\/30:hover{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.hover\:border-red-500\/30:hover{border-color:color-mix(in oklab,var(--color-red-500) 30%,transparent)}}.hover\:bg-accent-hover:hover{background-color:var(--color-accent-hover)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.hover\:bg-surface-600:hover{background-color:var(--color-surface-600)}.hover\:bg-surface-700:hover{background-color:var(--color-surface-700)}.hover\:bg-yellow-500\/10:hover{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-500\/10:hover{background-color:color-mix(in oklab,var(--color-yellow-500) 10%,transparent)}}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:64rem){.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}html{background-color:var(--color-surface-900);color:var(--color-text-primary);font-family:var(--font-body)}body{min-height:100dvh}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:var(--color-surface-900)}::-webkit-scrollbar-thumb{background:var(--color-surface-600);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--color-accent)}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}} diff --git a/.svelte-kit/output/server/chunks/api.js b/.svelte-kit/output/server/chunks/api.js new file mode 100644 index 0000000..5e6cc8c --- /dev/null +++ b/.svelte-kit/output/server/chunks/api.js @@ -0,0 +1,69 @@ +import { b as private_env } from "./shared-server.js"; +import "./index2.js"; +const CACHE_TTL = 10 * 60 * 1e3; +const MAX_ENTRIES = 500; +const cache = /* @__PURE__ */ new Map(); +function getCached(key) { + const entry = cache.get(key); + if (!entry) return null; + if (Date.now() > entry.expiresAt) { + cache.delete(key); + return null; + } + return entry.data; +} +function setCache(key, data, ttl = CACHE_TTL) { + if (cache.size >= MAX_ENTRIES) { + const firstKey = cache.keys().next().value; + if (firstKey) cache.delete(firstKey); + } + cache.set(key, { data, expiresAt: Date.now() + ttl }); +} +let clientToken = null; +let clientTokenExpiresAt = 0; +async function getClientToken() { + if (clientToken && Date.now() < clientTokenExpiresAt - 6e4) { + return clientToken; + } + const res = await fetch("https://osu.ppy.sh/oauth/token", { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + client_id: private_env.OSU_CLIENT_ID, + client_secret: private_env.OSU_CLIENT_SECRET, + grant_type: "client_credentials", + scope: "identify public" + }) + }); + if (!res.ok) { + throw new Error(`Failed to get osu! client token: ${res.status} ${res.statusText}`); + } + const data = await res.json(); + clientToken = data.access_token; + clientTokenExpiresAt = Date.now() + data.expires_in * 1e3; + return clientToken; +} +async function osuFetch(path, token) { + const t = await getClientToken(); + const res = await fetch(`https://osu.ppy.sh/api/v2${path}`, { + headers: { Authorization: `Bearer ${t}` } + }); + if (!res.ok) { + throw new Error(`osu! API ${path}: ${res.status} ${res.statusText}`); + } + return res.json(); +} +async function getBeatmap(beatmapId) { + const cacheKey = `beatmap:${beatmapId}`; + const cached = getCached(cacheKey); + if (cached) return cached; + const data = await osuFetch(`/beatmaps/${beatmapId}`); + setCache(cacheKey, data); + return data; +} +export { + getBeatmap as g +}; diff --git a/.svelte-kit/output/server/chunks/auth.js b/.svelte-kit/output/server/chunks/auth.js new file mode 100644 index 0000000..588593c --- /dev/null +++ b/.svelte-kit/output/server/chunks/auth.js @@ -0,0 +1,7983 @@ +import { env, isTest, isDevelopment, logger, createLogger, isProduction, shouldPublishLog } from "@better-auth/core/env"; +import { BetterAuthError, BASE_ERROR_CODES } from "@better-auth/core/error"; +import { isValidIP, normalizeIP, deprecate, normalizePathname, createRateLimitKey, safeJSONParse, generateId, filterOutputFields, defineErrorCodes } from "@better-auth/core/utils"; +import { defineRequestState, getCurrentAuthContext, getCurrentAdapter, runWithTransaction, hasRequestState, runWithRequestState, runWithEndpointContext, runWithAdapter, getBetterAuthVersion } from "@better-auth/core/context"; +import { APIError, toResponse, createRouter } from "better-call"; +import { createAuthMiddleware, createAuthEndpoint } from "@better-auth/core/api"; +import * as import___better_auth_core_db from "@better-auth/core/db"; +import { getAuthTables } from "@better-auth/core/db"; +import * as z from "zod"; +import { Kysely, SqliteDialect, MysqlDialect, PostgresDialect, MssqlDialect, sql } from "kysely"; +import { initGetModelName, initGetFieldName, createAdapterFactory } from "@better-auth/core/db/adapter"; +import { hkdf } from "@noble/hashes/hkdf.js"; +import { sha256 } from "@noble/hashes/sha2.js"; +import { jwtDecrypt, calculateJwkThumbprint, base64url, jwtVerify, EncryptJWT, SignJWT, decodeJwt } from "jose"; +import { hex } from "@better-auth/utils/hex"; +import { createRandomStringGenerator } from "@better-auth/utils/random"; +import "@better-auth/utils"; +import { createHash } from "@better-auth/utils/hash"; +import { base64Url } from "@better-auth/utils/base64"; +import { binary } from "@better-auth/utils/binary"; +import { createHMAC } from "@better-auth/utils/hmac"; +import { xchacha20poly1305 } from "@noble/ciphers/chacha.js"; +import { hexToBytes as hexToBytes$1, managedNonce, utf8ToBytes, bytesToHex } from "@noble/ciphers/utils.js"; +import { SocialProviderListEnum, socialProviders } from "@better-auth/core/social-providers"; +import { JWTExpired } from "jose/errors"; +import defu$1, { createDefu, defu } from "defu"; +import { scryptAsync } from "@noble/hashes/scrypt.js"; +import { hexToBytes } from "@noble/hashes/utils.js"; +import { createTelemetry } from "@better-auth/telemetry"; +import { count, desc, asc, inArray, notInArray, like, lt, lte, ne, gt, gte, eq, and, or, sql as sql$1 } from "drizzle-orm"; +import { b as private_env } from "./shared-server.js"; +import "./url.js"; +import { getRequestEvent } from "@sveltejs/kit/internal/server"; +import "./root.js"; +import "./utils.js"; +import "@sveltejs/kit"; +import "@sveltejs/kit/internal"; +import "./query.js"; +import { d as db, c as team, t as teamMember } from "./index2.js"; +import { betterFetch } from "@better-fetch/fetch"; +import { createAuthorizationURL, validateAuthorizationCode, refreshAccessToken } from "@better-auth/core/oauth2"; +import "@better-auth/utils/otp"; +function checkHasPath(url) { + try { + return (new URL(url).pathname.replace(/\/+$/, "") || "/") !== "/"; + } catch { + throw new BetterAuthError(`Invalid base URL: ${url}. Please provide a valid base URL.`); + } +} +function assertHasProtocol(url) { + try { + const parsedUrl = new URL(url); + if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new BetterAuthError(`Invalid base URL: ${url}. URL must include 'http://' or 'https://'`); + } catch (error2) { + if (error2 instanceof BetterAuthError) throw error2; + throw new BetterAuthError(`Invalid base URL: ${url}. Please provide a valid base URL.`, { cause: error2 }); + } +} +function withPath(url, path = "/api/auth") { + assertHasProtocol(url); + if (checkHasPath(url)) return url; + const trimmedUrl = url.replace(/\/+$/, ""); + if (!path || path === "/") return trimmedUrl; + path = path.startsWith("/") ? path : `/${path}`; + return `${trimmedUrl}${path}`; +} +function validateProxyHeader(header, type) { + if (!header || header.trim() === "") return false; + if (type === "proto") return header === "http" || header === "https"; + if (type === "host") { + if ([ + /\.\./, + /\0/, + /[\s]/, + /^[.]/, + /[<>'"]/, + /javascript:/i, + /file:/i, + /data:/i + ].some((pattern) => pattern.test(header))) return false; + return /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*(:[0-9]{1,5})?$/.test(header) || /^(\d{1,3}\.){3}\d{1,3}(:[0-9]{1,5})?$/.test(header) || /^\[[0-9a-fA-F:]+\](:[0-9]{1,5})?$/.test(header) || /^localhost(:[0-9]{1,5})?$/i.test(header); + } + return false; +} +function getBaseURL(url, path, request, loadEnv, trustedProxyHeaders) { + if (url) return withPath(url, path); + { + const fromEnv = env.BETTER_AUTH_URL || env.NEXT_PUBLIC_BETTER_AUTH_URL || env.PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_AUTH_URL || (env.BASE_URL !== "/" ? env.BASE_URL : void 0); + if (fromEnv) return withPath(fromEnv, path); + } + const fromRequest = request?.headers.get("x-forwarded-host"); + const fromRequestProto = request?.headers.get("x-forwarded-proto"); + if (fromRequest && fromRequestProto && trustedProxyHeaders) { + if (validateProxyHeader(fromRequestProto, "proto") && validateProxyHeader(fromRequest, "host")) try { + return withPath(`${fromRequestProto}://${fromRequest}`, path); + } catch (_error) { + } + } + if (request) { + const url$1 = getOrigin(request.url); + if (!url$1) throw new BetterAuthError("Could not get origin from request. Please provide a valid base URL."); + return withPath(url$1, path); + } + if (typeof window !== "undefined" && window.location) return withPath(window.location.origin, path); +} +function getOrigin(url) { + try { + const parsedUrl = new URL(url); + return parsedUrl.origin === "null" ? null : parsedUrl.origin; + } catch { + return null; + } +} +function getProtocol(url) { + try { + return new URL(url).protocol; + } catch { + return null; + } +} +function getHost(url) { + try { + return new URL(url).host; + } catch { + return null; + } +} +const LOCALHOST_IP = "127.0.0.1"; +function getIp(req, options) { + if (options.advanced?.ipAddress?.disableIpTracking) return null; + const headers = "headers" in req ? req.headers : req; + const ipHeaders = options.advanced?.ipAddress?.ipAddressHeaders || ["x-forwarded-for"]; + for (const key of ipHeaders) { + const value = "get" in headers ? headers.get(key) : headers[key]; + if (typeof value === "string") { + const ip = value.split(",")[0].trim(); + if (isValidIP(ip)) return normalizeIP(ip, { ipv6Subnet: options.advanced?.ipAddress?.ipv6Subnet }); + } + } + if (isTest() || isDevelopment()) return LOCALHOST_IP; + return null; +} +const { get: getOAuthState, set: setOAuthState } = defineRequestState(() => null); +function escapeRegExpChar(char) { + if (char === "-" || char === "^" || char === "$" || char === "+" || char === "." || char === "(" || char === ")" || char === "|" || char === "[" || char === "]" || char === "{" || char === "}" || char === "*" || char === "?" || char === "\\") return `\\${char}`; + else return char; +} +function escapeRegExpString(str) { + let result = ""; + for (let i = 0; i < str.length; i++) result += escapeRegExpChar(str[i]); + return result; +} +function transform(pattern, separator = true) { + if (Array.isArray(pattern)) return `(?:${pattern.map((p) => `^${transform(p, separator)}$`).join("|")})`; + let separatorSplitter = ""; + let separatorMatcher = ""; + let wildcard = "."; + if (separator === true) { + separatorSplitter = "/"; + separatorMatcher = "[/\\\\]"; + wildcard = "[^/\\\\]"; + } else if (separator) { + separatorSplitter = separator; + separatorMatcher = escapeRegExpString(separatorSplitter); + if (separatorMatcher.length > 1) { + separatorMatcher = `(?:${separatorMatcher})`; + wildcard = `((?!${separatorMatcher}).)`; + } else wildcard = `[^${separatorMatcher}]`; + } + const requiredSeparator = separator ? `${separatorMatcher}+?` : ""; + const optionalSeparator = separator ? `${separatorMatcher}*?` : ""; + const segments = separator ? pattern.split(separatorSplitter) : [pattern]; + let result = ""; + for (let s = 0; s < segments.length; s++) { + const segment = segments[s]; + const nextSegment = segments[s + 1]; + let currentSeparator = ""; + if (!segment && s > 0) continue; + if (separator) if (s === segments.length - 1) currentSeparator = optionalSeparator; + else if (nextSegment !== "**") currentSeparator = requiredSeparator; + else currentSeparator = ""; + if (separator && segment === "**") { + if (currentSeparator) { + result += s === 0 ? "" : currentSeparator; + result += `(?:${wildcard}*?${currentSeparator})*?`; + } + continue; + } + for (let c = 0; c < segment.length; c++) { + const char = segment[c]; + if (char === "\\") { + if (c < segment.length - 1) { + result += escapeRegExpChar(segment[c + 1]); + c++; + } + } else if (char === "?") result += wildcard; + else if (char === "*") result += `${wildcard}*?`; + else result += escapeRegExpChar(char); + } + result += currentSeparator; + } + return result; +} +function isMatch(regexp, sample) { + if (typeof sample !== "string") throw new TypeError(`Sample must be a string, but ${typeof sample} given`); + return regexp.test(sample); +} +function wildcardMatch(pattern, options) { + if (typeof pattern !== "string" && !Array.isArray(pattern)) throw new TypeError(`The first argument must be a single pattern string or an array of patterns, but ${typeof pattern} given`); + if (typeof options === "string" || typeof options === "boolean") options = { separator: options }; + if (arguments.length === 2 && !(typeof options === "undefined" || typeof options === "object" && options !== null && !Array.isArray(options))) throw new TypeError(`The second argument must be an options object or a string/boolean separator, but ${typeof options} given`); + options = options || {}; + if (options.separator === "\\") throw new Error("\\ is not a valid separator because it is used for escaping. Try setting the separator to `true` instead"); + const regexpPattern = transform(pattern, options.separator); + const regexp = new RegExp(`^${regexpPattern}$`, options.flags); + const fn = isMatch.bind(null, regexp); + fn.options = options; + fn.pattern = pattern; + fn.regexp = regexp; + return fn; +} +const matchesOriginPattern = (url, pattern, settings) => { + if (url.startsWith("/")) { + if (settings?.allowRelativePaths) return url.startsWith("/") && /^\/(?!\/|\\|%2f|%5c)[\w\-.\+/@]*(?:\?[\w\-.\+/=&%@]*)?$/.test(url); + return false; + } + if (pattern.includes("*") || pattern.includes("?")) { + if (pattern.includes("://")) return wildcardMatch(pattern)(getOrigin(url) || url); + const host = getHost(url); + if (!host) return false; + return wildcardMatch(pattern)(host); + } + const protocol = getProtocol(url); + return protocol === "http:" || protocol === "https:" || !protocol ? pattern === getOrigin(url) : url.startsWith(pattern); +}; +function shouldSkipCSRFForBackwardCompat(ctx) { + return ctx.context.skipOriginCheck === true && ctx.context.options.advanced?.disableCSRFCheck === void 0; +} +const logBackwardCompatWarning = deprecate(function logBackwardCompatWarning$1() { +}, "disableOriginCheck: true currently also disables CSRF checks. In a future version, disableOriginCheck will ONLY disable URL validation. To keep CSRF disabled, add disableCSRFCheck: true to your config."); +const originCheckMiddleware = createAuthMiddleware(async (ctx) => { + if (ctx.request?.method === "GET" || ctx.request?.method === "OPTIONS" || ctx.request?.method === "HEAD" || !ctx.request) return; + await validateOrigin(ctx); + if (ctx.context.skipOriginCheck) return; + const { body, query } = ctx; + const callbackURL = body?.callbackURL || query?.callbackURL; + const redirectURL = body?.redirectTo; + const errorCallbackURL = body?.errorCallbackURL; + const newUserCallbackURL = body?.newUserCallbackURL; + const validateURL = (url, label) => { + if (!url) return; + if (!ctx.context.isTrustedOrigin(url, { allowRelativePaths: label !== "origin" })) { + ctx.context.logger.error(`Invalid ${label}: ${url}`); + ctx.context.logger.info(`If it's a valid URL, please add ${url} to trustedOrigins in your auth config +`, `Current list of trustedOrigins: ${ctx.context.trustedOrigins}`); + throw new APIError("FORBIDDEN", { message: `Invalid ${label}` }); + } + }; + callbackURL && validateURL(callbackURL, "callbackURL"); + redirectURL && validateURL(redirectURL, "redirectURL"); + errorCallbackURL && validateURL(errorCallbackURL, "errorCallbackURL"); + newUserCallbackURL && validateURL(newUserCallbackURL, "newUserCallbackURL"); +}); +const originCheck = (getValue) => createAuthMiddleware(async (ctx) => { + if (!ctx.request) return; + if (ctx.context.skipOriginCheck) return; + const callbackURL = getValue(ctx); + const validateURL = (url, label) => { + if (!url) return; + if (!ctx.context.isTrustedOrigin(url, { allowRelativePaths: label !== "origin" })) { + ctx.context.logger.error(`Invalid ${label}: ${url}`); + ctx.context.logger.info(`If it's a valid URL, please add ${url} to trustedOrigins in your auth config +`, `Current list of trustedOrigins: ${ctx.context.trustedOrigins}`); + throw new APIError("FORBIDDEN", { message: `Invalid ${label}` }); + } + }; + const callbacks = Array.isArray(callbackURL) ? callbackURL : [callbackURL]; + for (const url of callbacks) validateURL(url, "callbackURL"); +}); +async function validateOrigin(ctx, forceValidate = false) { + const headers = ctx.request?.headers; + if (!headers || !ctx.request) return; + const originHeader = headers.get("origin") || headers.get("referer") || ""; + const useCookies = headers.has("cookie"); + if (ctx.context.skipCSRFCheck) return; + if (shouldSkipCSRFForBackwardCompat(ctx)) { + ctx.context.options.advanced?.disableOriginCheck === true && logBackwardCompatWarning(); + return; + } + const skipOriginCheck = ctx.context.skipOriginCheck; + if (Array.isArray(skipOriginCheck)) try { + const basePath = new URL(ctx.context.baseURL).pathname; + const currentPath = normalizePathname(ctx.request.url, basePath); + if (skipOriginCheck.some((skipPath) => currentPath.startsWith(skipPath))) return; + } catch { + } + if (!(forceValidate || useCookies)) return; + if (!originHeader || originHeader === "null") throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.MISSING_OR_NULL_ORIGIN }); + const trustedOrigins = Array.isArray(ctx.context.options.trustedOrigins) ? ctx.context.trustedOrigins : [...ctx.context.trustedOrigins, ...(await ctx.context.options.trustedOrigins?.(ctx.request))?.filter((v) => Boolean(v)) || []]; + if (!trustedOrigins.some((origin) => matchesOriginPattern(originHeader, origin))) { + ctx.context.logger.error(`Invalid origin: ${originHeader}`); + ctx.context.logger.info(`If it's a valid URL, please add ${originHeader} to trustedOrigins in your auth config +`, `Current list of trustedOrigins: ${trustedOrigins}`); + throw new APIError("FORBIDDEN", { message: "Invalid origin" }); + } +} +const formCsrfMiddleware = createAuthMiddleware(async (ctx) => { + if (!ctx.request) return; + await validateFormCsrf(ctx); +}); +async function validateFormCsrf(ctx) { + const req = ctx.request; + if (!req) return; + if (ctx.context.skipCSRFCheck) return; + if (shouldSkipCSRFForBackwardCompat(ctx)) return; + const headers = req.headers; + if (headers.has("cookie")) return await validateOrigin(ctx); + const site = headers.get("Sec-Fetch-Site"); + const mode = headers.get("Sec-Fetch-Mode"); + const dest = headers.get("Sec-Fetch-Dest"); + if (Boolean(site && site.trim() || mode && mode.trim() || dest && dest.trim())) { + if (site === "cross-site" && mode === "navigate") { + ctx.context.logger.error("Blocked cross-site navigation login attempt (CSRF protection)", { + secFetchSite: site, + secFetchMode: mode, + secFetchDest: dest + }); + throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.CROSS_SITE_NAVIGATION_LOGIN_BLOCKED }); + } + return await validateOrigin(ctx, true); + } +} +const memory = /* @__PURE__ */ new Map(); +function shouldRateLimit(max, window2, rateLimitData) { + const now2 = Date.now(); + const windowInMs = window2 * 1e3; + return now2 - rateLimitData.lastRequest < windowInMs && rateLimitData.count >= max; +} +function rateLimitResponse(retryAfter) { + return new Response(JSON.stringify({ message: "Too many requests. Please try again later." }), { + status: 429, + statusText: "Too Many Requests", + headers: { "X-Retry-After": retryAfter.toString() } + }); +} +function getRetryAfter(lastRequest, window2) { + const now2 = Date.now(); + const windowInMs = window2 * 1e3; + return Math.ceil((lastRequest + windowInMs - now2) / 1e3); +} +function createDatabaseStorageWrapper(ctx) { + const model = "rateLimit"; + const db2 = ctx.adapter; + return { + get: async (key) => { + const data = (await db2.findMany({ + model, + where: [{ + field: "key", + value: key + }] + }))[0]; + if (typeof data?.lastRequest === "bigint") data.lastRequest = Number(data.lastRequest); + return data; + }, + set: async (key, value, _update) => { + try { + if (_update) await db2.updateMany({ + model, + where: [{ + field: "key", + value: key + }], + update: { + count: value.count, + lastRequest: value.lastRequest + } + }); + else await db2.create({ + model, + data: { + key, + count: value.count, + lastRequest: value.lastRequest + } + }); + } catch (e) { + ctx.logger.error("Error setting rate limit", e); + } + } + }; +} +function getRateLimitStorage(ctx, rateLimitSettings) { + if (ctx.options.rateLimit?.customStorage) return ctx.options.rateLimit.customStorage; + const storage = ctx.rateLimit.storage; + if (storage === "secondary-storage") return { + get: async (key) => { + const data = await ctx.options.secondaryStorage?.get(key); + return data ? safeJSONParse(data) : null; + }, + set: async (key, value, _update) => { + const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10; + await ctx.options.secondaryStorage?.set?.(key, JSON.stringify(value), ttl); + } + }; + else if (storage === "memory") return { + async get(key) { + const entry = memory.get(key); + if (!entry) return null; + if (Date.now() >= entry.expiresAt) { + memory.delete(key); + return null; + } + return entry.data; + }, + async set(key, value, _update) { + const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10; + const expiresAt = Date.now() + ttl * 1e3; + memory.set(key, { + data: value, + expiresAt + }); + } + }; + return createDatabaseStorageWrapper(ctx); +} +async function onRequestRateLimit(req, ctx) { + if (!ctx.rateLimit.enabled) return; + const basePath = new URL(ctx.baseURL).pathname; + const path = normalizePathname(req.url, basePath); + let currentWindow = ctx.rateLimit.window; + let currentMax = ctx.rateLimit.max; + const ip = getIp(req, ctx.options); + if (!ip) return; + const key = createRateLimitKey(ip, path); + const specialRule = getDefaultSpecialRules().find((rule) => rule.pathMatcher(path)); + if (specialRule) { + currentWindow = specialRule.window; + currentMax = specialRule.max; + } + for (const plugin of ctx.options.plugins || []) if (plugin.rateLimit) { + const matchedRule = plugin.rateLimit.find((rule) => rule.pathMatcher(path)); + if (matchedRule) { + currentWindow = matchedRule.window; + currentMax = matchedRule.max; + break; + } + } + if (ctx.rateLimit.customRules) { + const _path = Object.keys(ctx.rateLimit.customRules).find((p) => { + if (p.includes("*")) return wildcardMatch(p)(path); + return p === path; + }); + if (_path) { + const customRule = ctx.rateLimit.customRules[_path]; + const resolved = typeof customRule === "function" ? await customRule(req, { + window: currentWindow, + max: currentMax + }) : customRule; + if (resolved) { + currentWindow = resolved.window; + currentMax = resolved.max; + } + if (resolved === false) return; + } + } + const storage = getRateLimitStorage(ctx, { window: currentWindow }); + const data = await storage.get(key); + const now2 = Date.now(); + if (!data) await storage.set(key, { + key, + count: 1, + lastRequest: now2 + }); + else { + const timeSinceLastRequest = now2 - data.lastRequest; + if (shouldRateLimit(currentMax, currentWindow, data)) return rateLimitResponse(getRetryAfter(data.lastRequest, currentWindow)); + else if (timeSinceLastRequest > currentWindow * 1e3) await storage.set(key, { + ...data, + count: 1, + lastRequest: now2 + }, true); + else await storage.set(key, { + ...data, + count: data.count + 1, + lastRequest: now2 + }, true); + } +} +function getDefaultSpecialRules() { + return [{ + pathMatcher(path) { + return path.startsWith("/sign-in") || path.startsWith("/sign-up") || path.startsWith("/change-password") || path.startsWith("/change-email"); + }, + window: 10, + max: 3 + }]; +} +const getDate = (span, unit = "ms") => { + return new Date(Date.now() + (unit === "sec" ? span * 1e3 : span)); +}; +const cache = /* @__PURE__ */ new WeakMap(); +function parseOutputData(data, schema2) { + const fields = schema2.fields; + const parsedData = {}; + for (const key in data) { + const field = fields[key]; + if (!field) { + parsedData[key] = data[key]; + continue; + } + if (field.returned === false && key !== "id") continue; + parsedData[key] = data[key]; + } + return parsedData; +} +function getFields(options, table, mode) { + const cacheKey = `${table}:${mode}`; + if (!cache.has(options)) cache.set(options, /* @__PURE__ */ new Map()); + const tableCache = cache.get(options); + if (tableCache.has(cacheKey)) return tableCache.get(cacheKey); + const coreSchema = mode === "output" ? getAuthTables(options)[table]?.fields ?? {} : {}; + const additionalFields = table === "user" || table === "session" || table === "account" ? options[table]?.additionalFields : void 0; + let schema2 = { + ...coreSchema, + ...additionalFields ?? {} + }; + for (const plugin of options.plugins || []) if (plugin.schema && plugin.schema[table]) schema2 = { + ...schema2, + ...plugin.schema[table].fields + }; + tableCache.set(cacheKey, schema2); + return schema2; +} +function parseUserOutput(options, user) { + return parseOutputData(user, { fields: getFields(options, "user", "output") }); +} +function parseSessionOutput(options, session) { + return parseOutputData(session, { fields: getFields(options, "session", "output") }); +} +function parseAccountOutput(options, account) { + const { accessToken: _accessToken, refreshToken: _refreshToken, idToken: _idToken, accessTokenExpiresAt: _accessTokenExpiresAt, refreshTokenExpiresAt: _refreshTokenExpiresAt, password: _password, ...rest } = parseOutputData(account, { fields: getFields(options, "account", "output") }); + return rest; +} +function parseInputData(data, schema2) { + const action = schema2.action || "create"; + const fields = schema2.fields; + const parsedData = Object.assign(/* @__PURE__ */ Object.create(null), null); + for (const key in fields) { + if (key in data) { + if (fields[key].input === false) { + if (fields[key].defaultValue !== void 0) { + if (action !== "update") { + parsedData[key] = fields[key].defaultValue; + continue; + } + } + if (data[key]) throw new APIError("BAD_REQUEST", { message: `${key} is not allowed to be set` }); + continue; + } + if (fields[key].validator?.input && data[key] !== void 0) { + const result = fields[key].validator.input["~standard"].validate(data[key]); + if (result instanceof Promise) throw new APIError("INTERNAL_SERVER_ERROR", { message: "Async validation is not supported for additional fields" }); + if ("issues" in result && result.issues) throw new APIError("BAD_REQUEST", { message: result.issues[0]?.message || "Validation Error" }); + parsedData[key] = result.value; + continue; + } + if (fields[key].transform?.input && data[key] !== void 0) { + parsedData[key] = fields[key].transform?.input(data[key]); + continue; + } + parsedData[key] = data[key]; + continue; + } + if (fields[key].defaultValue !== void 0 && action === "create") { + if (typeof fields[key].defaultValue === "function") { + parsedData[key] = fields[key].defaultValue(); + continue; + } + parsedData[key] = fields[key].defaultValue; + continue; + } + if (fields[key].required && action === "create") throw new APIError("BAD_REQUEST", { message: `${key} is required` }); + } + return parsedData; +} +function parseUserInput(options, user = {}, action) { + return parseInputData(user, { + fields: getFields(options, "user", "input"), + action + }); +} +function parseAdditionalUserInput(options, user) { + const schema2 = getFields(options, "user", "input"); + return parseInputData(user || {}, { fields: schema2 }); +} +function parseAccountInput(options, account) { + return parseInputData(account, { fields: getFields(options, "account", "input") }); +} +function parseSessionInput(options, session) { + return parseInputData(session, { fields: getFields(options, "session", "input") }); +} +function mergeSchema(schema2, newSchema) { + if (!newSchema) return schema2; + for (const table in newSchema) { + const newModelName = newSchema[table]?.modelName; + if (newModelName) schema2[table].modelName = newModelName; + for (const field in schema2[table].fields) { + const newField = newSchema[table]?.fields?.[field]; + if (!newField) continue; + schema2[table].fields[field].fieldName = newField; + } + } + return schema2; +} +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (all, symbols) => { + let target = {}; + for (var name in all) { + __defProp(target, name, { + get: all[name], + enumerable: true + }); + } + return target; +}; +var __copyProps = (to, from, except, desc2) => { + if (from && typeof from === "object" || typeof from === "function") { + for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { + key = keys[i]; + if (!__hasOwnProp.call(to, key) && key !== except) { + __defProp(to, key, { + get: ((k) => from[k]).bind(null, key), + enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable + }); + } + } + } + return to; +}; +var __reExport = (target, mod, secondTarget, symbols) => { + __copyProps(target, mod, "default"); +}; +async function getBaseAdapter(options, handleDirectDatabase) { + let adapter; + if (!options.database) { + const tables = getAuthTables(options); + const memoryDB = Object.keys(tables).reduce((acc, key) => { + acc[key] = []; + return acc; + }, {}); + const { memoryAdapter } = await import("./index5.js"); + adapter = memoryAdapter(memoryDB)(options); + } else if (typeof options.database === "function") adapter = options.database(options); + else adapter = await handleDirectDatabase(options); + if (!adapter.transaction) { + logger.warn("Adapter does not correctly implement transaction function, patching it automatically. Please update your adapter implementation."); + adapter.transaction = async (cb) => { + return cb(adapter); + }; + } + return adapter; +} +async function getAdapter(options) { + return getBaseAdapter(options, async (opts) => { + const { createKyselyAdapter: createKyselyAdapter2 } = await import("./index6.js"); + const { kysely, databaseType, transaction } = await createKyselyAdapter2(opts); + if (!kysely) throw new BetterAuthError("Failed to initialize database adapter"); + const { kyselyAdapter } = await import("./index6.js"); + return kyselyAdapter(kysely, { + type: databaseType || "sqlite", + debugLogs: opts.database && "debugLogs" in opts.database ? opts.database.debugLogs : false, + transaction + })(opts); + }); +} +const createFieldAttribute = (type, config2) => { + return { + type, + ...config2 + }; +}; +function convertToDB(fields, values) { + const result = values.id ? { id: values.id } : {}; + for (const key in fields) { + const field = fields[key]; + const value = values[key]; + if (value === void 0) continue; + result[field.fieldName || key] = value; + } + return result; +} +function convertFromDB(fields, values) { + if (!values) return null; + const result = { id: values.id }; + for (const [key, value] of Object.entries(fields)) result[key] = values[value.fieldName || key]; + return result; +} +function getWithHooks(adapter, ctx) { + const hooks = ctx.hooks; + async function createWithHooks(data, model, customCreateFn) { + const context = await getCurrentAuthContext().catch(() => null); + let actualData = data; + for (const hook of hooks || []) { + const toRun = hook[model]?.create?.before; + if (toRun) { + const result = await toRun(actualData, context); + if (result === false) return null; + if (typeof result === "object" && "data" in result) actualData = { + ...actualData, + ...result.data + }; + } + } + const customCreated = customCreateFn ? await customCreateFn.fn(actualData) : null; + const created = !customCreateFn || customCreateFn.executeMainFn ? await (await getCurrentAdapter(adapter)).create({ + model, + data: actualData, + forceAllowId: true + }) : customCreated; + for (const hook of hooks || []) { + const toRun = hook[model]?.create?.after; + if (toRun) await toRun(created, context); + } + return created; + } + async function updateWithHooks(data, where, model, customUpdateFn) { + const context = await getCurrentAuthContext().catch(() => null); + let actualData = data; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.before; + if (toRun) { + const result = await toRun(data, context); + if (result === false) return null; + if (typeof result === "object" && "data" in result) actualData = { + ...actualData, + ...result.data + }; + } + } + const customUpdated = customUpdateFn ? await customUpdateFn.fn(actualData) : null; + const updated = !customUpdateFn || customUpdateFn.executeMainFn ? await (await getCurrentAdapter(adapter)).update({ + model, + update: actualData, + where + }) : customUpdated; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.after; + if (toRun) await toRun(updated, context); + } + return updated; + } + async function updateManyWithHooks(data, where, model, customUpdateFn) { + const context = await getCurrentAuthContext().catch(() => null); + let actualData = data; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.before; + if (toRun) { + const result = await toRun(data, context); + if (result === false) return null; + if (typeof result === "object" && "data" in result) actualData = { + ...actualData, + ...result.data + }; + } + } + const customUpdated = customUpdateFn ? await customUpdateFn.fn(actualData) : null; + const updated = !customUpdateFn || customUpdateFn.executeMainFn ? await (await getCurrentAdapter(adapter)).updateMany({ + model, + update: actualData, + where + }) : customUpdated; + for (const hook of hooks || []) { + const toRun = hook[model]?.update?.after; + if (toRun) await toRun(updated, context); + } + return updated; + } + async function deleteWithHooks(where, model, customDeleteFn) { + const context = await getCurrentAuthContext().catch(() => null); + let entityToDelete = null; + try { + entityToDelete = (await (await getCurrentAdapter(adapter)).findMany({ + model, + where, + limit: 1 + }))[0] || null; + } catch { + } + if (entityToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.before; + if (toRun) { + if (await toRun(entityToDelete, context) === false) return null; + } + } + const customDeleted = customDeleteFn ? await customDeleteFn.fn(where) : null; + const deleted = !customDeleteFn || customDeleteFn.executeMainFn ? await (await getCurrentAdapter(adapter)).delete({ + model, + where + }) : customDeleted; + if (entityToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.after; + if (toRun) await toRun(entityToDelete, context); + } + return deleted; + } + async function deleteManyWithHooks(where, model, customDeleteFn) { + const context = await getCurrentAuthContext().catch(() => null); + let entitiesToDelete = []; + try { + entitiesToDelete = await (await getCurrentAdapter(adapter)).findMany({ + model, + where + }); + } catch { + } + for (const entity of entitiesToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.before; + if (toRun) { + if (await toRun(entity, context) === false) return null; + } + } + const customDeleted = customDeleteFn ? await customDeleteFn.fn(where) : null; + const deleted = !customDeleteFn || customDeleteFn.executeMainFn ? await (await getCurrentAdapter(adapter)).deleteMany({ + model, + where + }) : customDeleted; + for (const entity of entitiesToDelete) for (const hook of hooks || []) { + const toRun = hook[model]?.delete?.after; + if (toRun) await toRun(entity, context); + } + return deleted; + } + return { + createWithHooks, + updateWithHooks, + updateManyWithHooks, + deleteWithHooks, + deleteManyWithHooks + }; +} +const createInternalAdapter = (adapter, ctx) => { + const logger2 = ctx.logger; + const options = ctx.options; + const secondaryStorage = options.secondaryStorage; + const sessionExpiration = options.session?.expiresIn || 3600 * 24 * 7; + const { createWithHooks, updateWithHooks, updateManyWithHooks, deleteWithHooks, deleteManyWithHooks } = getWithHooks(adapter, ctx); + async function refreshUserSessions(user) { + if (!secondaryStorage) return; + const listRaw = await secondaryStorage.get(`active-sessions-${user.id}`); + if (!listRaw) return; + const now2 = Date.now(); + const validSessions = (safeJSONParse(listRaw) || []).filter((s) => s.expiresAt > now2); + await Promise.all(validSessions.map(async ({ token }) => { + const cached = await secondaryStorage.get(token); + if (!cached) return; + const parsed = safeJSONParse(cached); + if (!parsed) return; + const sessionTTL = Math.max(Math.floor(new Date(parsed.session.expiresAt).getTime() - now2) / 1e3, 0); + await secondaryStorage.set(token, JSON.stringify({ + session: parsed.session, + user + }), Math.floor(sessionTTL)); + })); + } + return { + createOAuthUser: async (user, account) => { + return runWithTransaction(adapter, async () => { + const createdUser = await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...user + }, "user", void 0); + return { + user: createdUser, + account: await createWithHooks({ + ...account, + userId: createdUser.id, + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date() + }, "account", void 0) + }; + }); + }, + createUser: async (user) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...user, + email: user.email?.toLowerCase() + }, "user", void 0); + }, + createAccount: async (account) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...account + }, "account", void 0); + }, + listSessions: async (userId) => { + if (secondaryStorage) { + const currentList = await secondaryStorage.get(`active-sessions-${userId}`); + if (!currentList) return []; + const list = safeJSONParse(currentList) || []; + const now2 = Date.now(); + const seenTokens = /* @__PURE__ */ new Set(); + const sessions = []; + for (const { token, expiresAt } of list) { + if (expiresAt <= now2 || seenTokens.has(token)) continue; + seenTokens.add(token); + const data = await secondaryStorage.get(token); + if (!data) continue; + try { + const parsed = typeof data === "string" ? JSON.parse(data) : data; + if (!parsed?.session) continue; + sessions.push(parseSessionOutput(ctx.options, { + ...parsed.session, + expiresAt: new Date(parsed.session.expiresAt) + })); + } catch { + continue; + } + } + return sessions; + } + return await (await getCurrentAdapter(adapter)).findMany({ + model: "session", + where: [{ + field: "userId", + value: userId + }] + }); + }, + listUsers: async (limit, offset, sortBy, where) => { + return await (await getCurrentAdapter(adapter)).findMany({ + model: "user", + limit, + offset, + sortBy, + where + }); + }, + countTotalUsers: async (where) => { + const total = await (await getCurrentAdapter(adapter)).count({ + model: "user", + where + }); + if (typeof total === "string") return parseInt(total); + return total; + }, + deleteUser: async (userId) => { + if (!secondaryStorage || options.session?.storeSessionInDatabase) await deleteManyWithHooks([{ + field: "userId", + value: userId + }], "session", void 0); + await deleteManyWithHooks([{ + field: "userId", + value: userId + }], "account", void 0); + await deleteWithHooks([{ + field: "id", + value: userId + }], "user", void 0); + }, + createSession: async (userId, dontRememberMe, override, overrideAll) => { + const ctx$1 = await getCurrentAuthContext().catch(() => null); + const headers = ctx$1?.headers || ctx$1?.request?.headers; + const { id: _, ...rest } = override || {}; + const defaultAdditionalFields = parseSessionInput(ctx$1?.context.options ?? options, {}); + const data = { + ipAddress: ctx$1?.request || ctx$1?.headers ? getIp(ctx$1?.request || ctx$1?.headers, ctx$1?.context.options) || "" : "", + userAgent: headers?.get("user-agent") || "", + ...rest, + expiresAt: dontRememberMe ? getDate(3600 * 24, "sec") : getDate(sessionExpiration, "sec"), + userId, + token: generateId(32), + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...defaultAdditionalFields, + ...overrideAll ? rest : {} + }; + return await createWithHooks(data, "session", secondaryStorage ? { + fn: async (sessionData) => { + const currentList = await secondaryStorage.get(`active-sessions-${userId}`); + let list = []; + const now2 = Date.now(); + if (currentList) { + list = safeJSONParse(currentList) || []; + list = list.filter((session) => session.expiresAt > now2 && session.token !== data.token); + } + const sorted = [...list, { + token: data.token, + expiresAt: data.expiresAt.getTime() + }].sort((a, b) => a.expiresAt - b.expiresAt); + const furthestSessionExp = sorted.at(-1)?.expiresAt ?? data.expiresAt.getTime(); + const furthestSessionTTL = Math.max(Math.floor((furthestSessionExp - now2) / 1e3), 0); + if (furthestSessionTTL > 0) await secondaryStorage.set(`active-sessions-${userId}`, JSON.stringify(sorted), furthestSessionTTL); + const user = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + field: "id", + value: userId + }] + }); + const sessionTTL = Math.max(Math.floor((data.expiresAt.getTime() - now2) / 1e3), 0); + if (sessionTTL > 0) await secondaryStorage.set(data.token, JSON.stringify({ + session: sessionData, + user + }), sessionTTL); + return sessionData; + }, + executeMainFn: options.session?.storeSessionInDatabase + } : void 0); + }, + findSession: async (token) => { + if (secondaryStorage) { + const sessionStringified = await secondaryStorage.get(token); + if (!sessionStringified && !options.session?.storeSessionInDatabase) return null; + if (sessionStringified) { + const s = safeJSONParse(sessionStringified); + if (!s) return null; + return { + session: parseSessionOutput(ctx.options, { + ...s.session, + expiresAt: new Date(s.session.expiresAt), + createdAt: new Date(s.session.createdAt), + updatedAt: new Date(s.session.updatedAt) + }), + user: parseUserOutput(ctx.options, { + ...s.user, + createdAt: new Date(s.user.createdAt), + updatedAt: new Date(s.user.updatedAt) + }) + }; + } + } + const result = await (await getCurrentAdapter(adapter)).findOne({ + model: "session", + where: [{ + value: token, + field: "token" + }], + join: { user: true } + }); + if (!result) return null; + const { user, ...session } = result; + if (!user) return null; + return { + session: parseSessionOutput(ctx.options, session), + user: parseUserOutput(ctx.options, user) + }; + }, + findSessions: async (sessionTokens) => { + if (secondaryStorage) { + const sessions$1 = []; + for (const sessionToken of sessionTokens) { + const sessionStringified = await secondaryStorage.get(sessionToken); + if (sessionStringified) try { + const s = typeof sessionStringified === "string" ? JSON.parse(sessionStringified) : sessionStringified; + if (!s?.session) continue; + const session = { + session: { + ...s.session, + expiresAt: new Date(s.session.expiresAt) + }, + user: { + ...s.user, + createdAt: new Date(s.user.createdAt), + updatedAt: new Date(s.user.updatedAt) + } + }; + sessions$1.push(session); + } catch { + continue; + } + } + return sessions$1; + } + const sessions = await (await getCurrentAdapter(adapter)).findMany({ + model: "session", + where: [{ + field: "token", + value: sessionTokens, + operator: "in" + }], + join: { user: true } + }); + if (!sessions.length) return []; + if (sessions.some((session) => !session.user)) return []; + return sessions.map((_session) => { + const { user, ...session } = _session; + return { + session, + user + }; + }); + }, + updateSession: async (sessionToken, session) => { + return await updateWithHooks(session, [{ + field: "token", + value: sessionToken + }], "session", secondaryStorage ? { + async fn(data) { + const currentSession = await secondaryStorage.get(sessionToken); + if (!currentSession) return null; + const parsedSession = safeJSONParse(currentSession); + if (!parsedSession) return null; + const mergedSession = { + ...parsedSession.session, + ...data, + expiresAt: new Date(data.expiresAt ?? parsedSession.session.expiresAt), + createdAt: new Date(parsedSession.session.createdAt), + updatedAt: new Date(data.updatedAt ?? parsedSession.session.updatedAt) + }; + const updatedSession = parseSessionOutput(ctx.options, mergedSession); + const now2 = Date.now(); + const expiresMs = new Date(updatedSession.expiresAt).getTime(); + const sessionTTL = Math.max(Math.floor((expiresMs - now2) / 1e3), 0); + if (sessionTTL > 0) { + await secondaryStorage.set(sessionToken, JSON.stringify({ + session: updatedSession, + user: parsedSession.user + }), sessionTTL); + const listKey = `active-sessions-${updatedSession.userId}`; + const listRaw = await secondaryStorage.get(listKey); + const sorted = (listRaw ? safeJSONParse(listRaw) || [] : []).filter((s) => s.token !== sessionToken && s.expiresAt > now2).concat([{ + token: sessionToken, + expiresAt: expiresMs + }]).sort((a, b) => a.expiresAt - b.expiresAt); + const furthestSessionExp = sorted.at(-1)?.expiresAt; + if (furthestSessionExp && furthestSessionExp > now2) await secondaryStorage.set(listKey, JSON.stringify(sorted), Math.floor((furthestSessionExp - now2) / 1e3)); + else await secondaryStorage.delete(listKey); + } + return updatedSession; + }, + executeMainFn: options.session?.storeSessionInDatabase + } : void 0); + }, + deleteSession: async (token) => { + if (secondaryStorage) { + const data = await secondaryStorage.get(token); + if (data) { + const { session } = safeJSONParse(data) ?? {}; + if (!session) { + logger2.error("Session not found in secondary storage"); + return; + } + const userId = session.userId; + const currentList = await secondaryStorage.get(`active-sessions-${userId}`); + if (currentList) { + const list = safeJSONParse(currentList) || []; + const now2 = Date.now(); + const filtered = list.filter((session$1) => session$1.expiresAt > now2 && session$1.token !== token); + const furthestSessionExp = filtered.sort((a, b) => a.expiresAt - b.expiresAt).at(-1)?.expiresAt; + if (filtered.length > 0 && furthestSessionExp && furthestSessionExp > Date.now()) await secondaryStorage.set(`active-sessions-${userId}`, JSON.stringify(filtered), Math.floor((furthestSessionExp - now2) / 1e3)); + else await secondaryStorage.delete(`active-sessions-${userId}`); + } else logger2.error("Active sessions list not found in secondary storage"); + } + await secondaryStorage.delete(token); + if (!options.session?.storeSessionInDatabase || ctx.options.session?.preserveSessionInDatabase) return; + } + await deleteWithHooks([{ + field: "token", + value: token + }], "session", void 0); + }, + deleteAccounts: async (userId) => { + await deleteManyWithHooks([{ + field: "userId", + value: userId + }], "account", void 0); + }, + deleteAccount: async (accountId) => { + await deleteWithHooks([{ + field: "id", + value: accountId + }], "account", void 0); + }, + deleteSessions: async (userIdOrSessionTokens) => { + if (secondaryStorage) { + if (typeof userIdOrSessionTokens === "string") { + const activeSession = await secondaryStorage.get(`active-sessions-${userIdOrSessionTokens}`); + const sessions = activeSession ? safeJSONParse(activeSession) : []; + if (!sessions) return; + for (const session of sessions) await secondaryStorage.delete(session.token); + await secondaryStorage.delete(`active-sessions-${userIdOrSessionTokens}`); + } else for (const sessionToken of userIdOrSessionTokens) if (await secondaryStorage.get(sessionToken)) await secondaryStorage.delete(sessionToken); + if (!options.session?.storeSessionInDatabase || ctx.options.session?.preserveSessionInDatabase) return; + } + await deleteManyWithHooks([{ + field: Array.isArray(userIdOrSessionTokens) ? "token" : "userId", + value: userIdOrSessionTokens, + operator: Array.isArray(userIdOrSessionTokens) ? "in" : void 0 + }], "session", void 0); + }, + findOAuthUser: async (email, accountId, providerId) => { + const account = await (await getCurrentAdapter(adapter)).findOne({ + model: "account", + where: [{ + value: accountId, + field: "accountId" + }, { + value: providerId, + field: "providerId" + }], + join: { user: true } + }); + if (account) if (account.user) return { + user: account.user, + linkedAccount: account, + accounts: [account] + }; + else { + const user = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + value: email.toLowerCase(), + field: "email" + }] + }); + if (user) return { + user, + linkedAccount: account, + accounts: [account] + }; + return null; + } + else { + const user = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + value: email.toLowerCase(), + field: "email" + }] + }); + if (user) return { + user, + linkedAccount: null, + accounts: await (await getCurrentAdapter(adapter)).findMany({ + model: "account", + where: [{ + value: user.id, + field: "userId" + }] + }) || [] + }; + else return null; + } + }, + findUserByEmail: async (email, options$1) => { + const result = await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + value: email.toLowerCase(), + field: "email" + }], + join: { ...options$1?.includeAccounts ? { account: true } : {} } + }); + if (!result) return null; + const { account: accounts, ...user } = result; + return { + user, + accounts: accounts ?? [] + }; + }, + findUserById: async (userId) => { + if (!userId) return null; + return await (await getCurrentAdapter(adapter)).findOne({ + model: "user", + where: [{ + field: "id", + value: userId + }] + }); + }, + linkAccount: async (account) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...account + }, "account", void 0); + }, + updateUser: async (userId, data) => { + const user = await updateWithHooks(data, [{ + field: "id", + value: userId + }], "user", void 0); + await refreshUserSessions(user); + return user; + }, + updateUserByEmail: async (email, data) => { + const user = await updateWithHooks(data, [{ + field: "email", + value: email.toLowerCase() + }], "user", void 0); + await refreshUserSessions(user); + return user; + }, + updatePassword: async (userId, password) => { + await updateManyWithHooks({ password }, [{ + field: "userId", + value: userId + }, { + field: "providerId", + value: "credential" + }], "account", void 0); + }, + findAccounts: async (userId) => { + return await (await getCurrentAdapter(adapter)).findMany({ + model: "account", + where: [{ + field: "userId", + value: userId + }] + }); + }, + findAccount: async (accountId) => { + return await (await getCurrentAdapter(adapter)).findOne({ + model: "account", + where: [{ + field: "accountId", + value: accountId + }] + }); + }, + findAccountByProviderId: async (accountId, providerId) => { + return await (await getCurrentAdapter(adapter)).findOne({ + model: "account", + where: [{ + field: "accountId", + value: accountId + }, { + field: "providerId", + value: providerId + }] + }); + }, + findAccountByUserId: async (userId) => { + return await (await getCurrentAdapter(adapter)).findMany({ + model: "account", + where: [{ + field: "userId", + value: userId + }] + }); + }, + updateAccount: async (id, data) => { + return await updateWithHooks(data, [{ + field: "id", + value: id + }], "account", void 0); + }, + createVerificationValue: async (data) => { + return await createWithHooks({ + createdAt: /* @__PURE__ */ new Date(), + updatedAt: /* @__PURE__ */ new Date(), + ...data + }, "verification", void 0); + }, + findVerificationValue: async (identifier) => { + const verification = await (await getCurrentAdapter(adapter)).findMany({ + model: "verification", + where: [{ + field: "identifier", + value: identifier + }], + sortBy: { + field: "createdAt", + direction: "desc" + }, + limit: 1 + }); + if (!options.verification?.disableCleanup) await deleteManyWithHooks([{ + field: "expiresAt", + value: /* @__PURE__ */ new Date(), + operator: "lt" + }], "verification", void 0); + return verification[0]; + }, + deleteVerificationValue: async (id) => { + await deleteWithHooks([{ + field: "id", + value: id + }], "verification", void 0); + }, + deleteVerificationByIdentifier: async (identifier) => { + await deleteWithHooks([{ + field: "identifier", + value: identifier + }], "verification", void 0); + }, + updateVerificationValue: async (id, data) => { + return await updateWithHooks(data, [{ + field: "id", + value: id + }], "verification", void 0); + } + }; +}; +function toZodSchema({ fields, isClientSide }) { + const zodFields = Object.keys(fields).reduce((acc, key) => { + const field = fields[key]; + if (!field) return acc; + if (isClientSide && field.input === false) return acc; + let schema2; + if (field.type === "json") schema2 = z.json ? z.json() : z.any(); + else if (field.type === "string[]" || field.type === "number[]") schema2 = z.array(field.type === "string[]" ? z.string() : z.number()); + else if (Array.isArray(field.type)) schema2 = z.any(); + else schema2 = z[field.type](); + if (field?.required === false) schema2 = schema2.optional(); + if (!isClientSide && field?.returned === false) return acc; + return { + ...acc, + [key]: schema2 + }; + }, {}); + return z.object(zodFields); +} +function getSchema(config2) { + const tables = (0, db_exports.getAuthTables)(config2); + const schema2 = {}; + for (const key in tables) { + const table = tables[key]; + const fields = table.fields; + const actualFields = {}; + Object.entries(fields).forEach(([key$1, field]) => { + actualFields[field.fieldName || key$1] = field; + if (field.references) { + const refTable = tables[field.references.model]; + if (refTable) actualFields[field.fieldName || key$1].references = { + ...field.references, + model: refTable.modelName, + field: field.references.field + }; + } + }); + if (schema2[table.modelName]) { + schema2[table.modelName].fields = { + ...schema2[table.modelName].fields, + ...actualFields + }; + continue; + } + schema2[table.modelName] = { + fields: actualFields, + order: table.order || Infinity + }; + } + return schema2; +} +function getKyselyDatabaseType(db2) { + if (!db2) return null; + if ("dialect" in db2) return getKyselyDatabaseType(db2.dialect); + if ("createDriver" in db2) { + if (db2 instanceof SqliteDialect) return "sqlite"; + if (db2 instanceof MysqlDialect) return "mysql"; + if (db2 instanceof PostgresDialect) return "postgres"; + if (db2 instanceof MssqlDialect) return "mssql"; + } + if ("aggregate" in db2) return "sqlite"; + if ("getConnection" in db2) return "mysql"; + if ("connect" in db2) return "postgres"; + if ("fileControl" in db2) return "sqlite"; + if ("open" in db2 && "close" in db2 && "prepare" in db2) return "sqlite"; + return null; +} +const createKyselyAdapter = async (config2) => { + const db2 = config2.database; + if (!db2) return { + kysely: null, + databaseType: null, + transaction: void 0 + }; + if ("db" in db2) return { + kysely: db2.db, + databaseType: db2.type, + transaction: db2.transaction + }; + if ("dialect" in db2) return { + kysely: new Kysely({ dialect: db2.dialect }), + databaseType: db2.type, + transaction: db2.transaction + }; + let dialect = void 0; + const databaseType = getKyselyDatabaseType(db2); + if ("createDriver" in db2) dialect = db2; + if ("aggregate" in db2 && !("createSession" in db2)) dialect = new SqliteDialect({ database: db2 }); + if ("getConnection" in db2) dialect = new MysqlDialect(db2); + if ("connect" in db2) dialect = new PostgresDialect({ pool: db2 }); + if ("fileControl" in db2) { + const { BunSqliteDialect } = await import("./bun-sqlite-dialect.js"); + dialect = new BunSqliteDialect({ database: db2 }); + } + if ("createSession" in db2) { + let DatabaseSync = void 0; + try { + const nodeSqlite = "node:sqlite"; + ({ DatabaseSync } = await import( + /* @vite-ignore */ + /* webpackIgnore: true */ + nodeSqlite + )); + } catch (error2) { + if (error2 !== null && typeof error2 === "object" && "code" in error2 && error2.code !== "ERR_UNKNOWN_BUILTIN_MODULE") throw error2; + } + if (DatabaseSync && db2 instanceof DatabaseSync) { + const { NodeSqliteDialect } = await import("./node-sqlite-dialect.js"); + dialect = new NodeSqliteDialect({ database: db2 }); + } + } + return { + kysely: dialect ? new Kysely({ dialect }) : null, + databaseType, + transaction: void 0 + }; +}; +const map = { + postgres: { + string: [ + "character varying", + "varchar", + "text", + "uuid" + ], + number: [ + "int4", + "integer", + "bigint", + "smallint", + "numeric", + "real", + "double precision" + ], + boolean: ["bool", "boolean"], + date: [ + "timestamptz", + "timestamp", + "date" + ], + json: ["json", "jsonb"] + }, + mysql: { + string: [ + "varchar", + "text", + "uuid" + ], + number: [ + "integer", + "int", + "bigint", + "smallint", + "decimal", + "float", + "double" + ], + boolean: ["boolean", "tinyint"], + date: [ + "timestamp", + "datetime", + "date" + ], + json: ["json"] + }, + sqlite: { + string: ["TEXT"], + number: ["INTEGER", "REAL"], + boolean: ["INTEGER", "BOOLEAN"], + date: ["DATE", "INTEGER"], + json: ["TEXT"] + }, + mssql: { + string: [ + "varchar", + "nvarchar", + "uniqueidentifier" + ], + number: [ + "int", + "bigint", + "smallint", + "decimal", + "float", + "double" + ], + boolean: ["bit", "smallint"], + date: [ + "datetime2", + "date", + "datetime" + ], + json: ["varchar", "nvarchar"] + } +}; +function matchType(columnDataType, fieldType, dbType) { + function normalize(type) { + return type.toLowerCase().split("(")[0].trim(); + } + if (fieldType === "string[]" || fieldType === "number[]") return columnDataType.toLowerCase().includes("json"); + const types2 = map[dbType]; + return (Array.isArray(fieldType) ? types2["string"].map((t) => t.toLowerCase()) : types2[fieldType].map((t) => t.toLowerCase())).includes(normalize(columnDataType)); +} +async function getPostgresSchema(db2) { + try { + const result = await sql`SHOW search_path`.execute(db2); + if (result.rows[0]?.search_path) return result.rows[0].search_path.split(",").map((s) => s.trim()).map((s) => s.replace(/^["']|["']$/g, "")).filter((s) => !s.startsWith("$"))[0] || "public"; + } catch { + } + return "public"; +} +async function getMigrations(config2) { + const betterAuthSchema = getSchema(config2); + const logger$1 = createLogger(config2.logger); + let { kysely: db2, databaseType: dbType } = await createKyselyAdapter(config2); + if (!dbType) { + logger$1.warn("Could not determine database type, defaulting to sqlite. Please provide a type in the database options to avoid this."); + dbType = "sqlite"; + } + if (!db2) { + logger$1.error("Only kysely adapter is supported for migrations. You can use `generate` command to generate the schema, if you're using a different adapter."); + process.exit(1); + } + let currentSchema = "public"; + if (dbType === "postgres") { + currentSchema = await getPostgresSchema(db2); + logger$1.debug(`PostgreSQL migration: Using schema '${currentSchema}' (from search_path)`); + try { + if (!(await sql` + SELECT schema_name + FROM information_schema.schemata + WHERE schema_name = ${currentSchema} + `.execute(db2)).rows[0]) logger$1.warn(`Schema '${currentSchema}' does not exist. Tables will be inspected from available schemas. Consider creating the schema first or checking your database configuration.`); + } catch (error2) { + logger$1.debug(`Could not verify schema existence: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + } + const allTableMetadata = await db2.introspection.getTables(); + let tableMetadata = allTableMetadata; + if (dbType === "postgres") try { + const tablesInSchema = await sql` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = ${currentSchema} + AND table_type = 'BASE TABLE' + `.execute(db2); + const tableNamesInSchema = new Set(tablesInSchema.rows.map((row) => row.table_name)); + tableMetadata = allTableMetadata.filter((table) => table.schema === currentSchema && tableNamesInSchema.has(table.name)); + logger$1.debug(`Found ${tableMetadata.length} table(s) in schema '${currentSchema}': ${tableMetadata.map((t) => t.name).join(", ") || "(none)"}`); + } catch (error2) { + logger$1.warn(`Could not filter tables by schema. Using all discovered tables. Error: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + const toBeCreated = []; + const toBeAdded = []; + for (const [key, value] of Object.entries(betterAuthSchema)) { + const table = tableMetadata.find((t) => t.name === key); + if (!table) { + const tIndex = toBeCreated.findIndex((t) => t.table === key); + const tableData = { + table: key, + fields: value.fields, + order: value.order || Infinity + }; + const insertIndex = toBeCreated.findIndex((t) => (t.order || Infinity) > tableData.order); + if (insertIndex === -1) if (tIndex === -1) toBeCreated.push(tableData); + else toBeCreated[tIndex].fields = { + ...toBeCreated[tIndex].fields, + ...value.fields + }; + else toBeCreated.splice(insertIndex, 0, tableData); + continue; + } + const toBeAddedFields = {}; + for (const [fieldName, field] of Object.entries(value.fields)) { + const column = table.columns.find((c) => c.name === fieldName); + if (!column) { + toBeAddedFields[fieldName] = field; + continue; + } + if (matchType(column.dataType, field.type, dbType)) continue; + else logger$1.warn(`Field ${fieldName} in table ${key} has a different type in the database. Expected ${field.type} but got ${column.dataType}.`); + } + if (Object.keys(toBeAddedFields).length > 0) toBeAdded.push({ + table: key, + fields: toBeAddedFields, + order: value.order || Infinity + }); + } + const migrations = []; + const useUUIDs = config2.advanced?.database?.generateId === "uuid"; + const useNumberId = config2.advanced?.database?.useNumberId || config2.advanced?.database?.generateId === "serial"; + function getType(field, fieldName) { + const type = field.type; + const provider = dbType || "sqlite"; + const typeMap = { + string: { + sqlite: "text", + postgres: "text", + mysql: field.unique ? "varchar(255)" : field.references ? "varchar(36)" : field.sortable ? "varchar(255)" : field.index ? "varchar(255)" : "text", + mssql: field.unique || field.sortable ? "varchar(255)" : field.references ? "varchar(36)" : "varchar(8000)" + }, + boolean: { + sqlite: "integer", + postgres: "boolean", + mysql: "boolean", + mssql: "smallint" + }, + number: { + sqlite: field.bigint ? "bigint" : "integer", + postgres: field.bigint ? "bigint" : "integer", + mysql: field.bigint ? "bigint" : "integer", + mssql: field.bigint ? "bigint" : "integer" + }, + date: { + sqlite: "date", + postgres: "timestamptz", + mysql: "timestamp(3)", + mssql: sql`datetime2(3)` + }, + json: { + sqlite: "text", + postgres: "jsonb", + mysql: "json", + mssql: "varchar(8000)" + }, + id: { + postgres: useNumberId ? sql`integer GENERATED BY DEFAULT AS IDENTITY` : useUUIDs ? "uuid" : "text", + mysql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + mssql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + sqlite: useNumberId ? "integer" : "text" + }, + foreignKeyId: { + postgres: useNumberId ? "integer" : useUUIDs ? "uuid" : "text", + mysql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + mssql: useNumberId ? "integer" : useUUIDs ? "varchar(36)" : "varchar(36)", + sqlite: useNumberId ? "integer" : "text" + }, + "string[]": { + sqlite: "text", + postgres: "jsonb", + mysql: "json", + mssql: "varchar(8000)" + }, + "number[]": { + sqlite: "text", + postgres: "jsonb", + mysql: "json", + mssql: "varchar(8000)" + } + }; + if (fieldName === "id" || field.references?.field === "id") { + if (fieldName === "id") return typeMap.id[provider]; + return typeMap.foreignKeyId[provider]; + } + if (Array.isArray(type)) return "text"; + if (!(type in typeMap)) throw new Error(`Unsupported field type '${String(type)}' for field '${fieldName}'. Allowed types are: string, number, boolean, date, string[], number[]. If you need to store structured data, store it as a JSON string (type: "string") or split it into primitive fields. See https://better-auth.com/docs/advanced/schema#additional-fields`); + return typeMap[type][provider]; + } + const getModelName = initGetModelName({ + schema: getAuthTables(config2), + usePlural: false + }); + const getFieldName = initGetFieldName({ + schema: getAuthTables(config2), + usePlural: false + }); + function getReferencePath(model, field) { + try { + return `${getModelName(model)}.${getFieldName({ + model, + field + })}`; + } catch { + return `${model}.${field}`; + } + } + if (toBeAdded.length) for (const table of toBeAdded) for (const [fieldName, field] of Object.entries(table.fields)) { + const type = getType(field, fieldName); + const builder = db2.schema.alterTable(table.table); + if (field.index) { + const index = db2.schema.alterTable(table.table).addIndex(`${table.table}_${fieldName}_idx`); + migrations.push(index); + } + const built = builder.addColumn(fieldName, type, (col) => { + col = field.required !== false ? col.notNull() : col; + if (field.references) col = col.references(getReferencePath(field.references.model, field.references.field)).onDelete(field.references.onDelete || "cascade"); + if (field.unique) col = col.unique(); + if (field.type === "date" && typeof field.defaultValue === "function" && (dbType === "postgres" || dbType === "mysql" || dbType === "mssql")) if (dbType === "mysql") col = col.defaultTo(sql`CURRENT_TIMESTAMP(3)`); + else col = col.defaultTo(sql`CURRENT_TIMESTAMP`); + return col; + }); + migrations.push(built); + } + const toBeIndexed = []; + if (config2.advanced?.database?.useNumberId) logger$1.warn("`useNumberId` is deprecated. Please use `generateId` with `serial` instead."); + if (toBeCreated.length) for (const table of toBeCreated) { + const idType = getType({ type: useNumberId ? "number" : "string" }, "id"); + let dbT = db2.schema.createTable(table.table).addColumn("id", idType, (col) => { + if (useNumberId) { + if (dbType === "postgres") return col.primaryKey().notNull(); + else if (dbType === "sqlite") return col.primaryKey().notNull(); + else if (dbType === "mssql") return col.identity().primaryKey().notNull(); + return col.autoIncrement().primaryKey().notNull(); + } + if (useUUIDs) { + if (dbType === "postgres") return col.primaryKey().defaultTo(sql`pg_catalog.gen_random_uuid()`).notNull(); + return col.primaryKey().notNull(); + } + return col.primaryKey().notNull(); + }); + for (const [fieldName, field] of Object.entries(table.fields)) { + const type = getType(field, fieldName); + dbT = dbT.addColumn(fieldName, type, (col) => { + col = field.required !== false ? col.notNull() : col; + if (field.references) col = col.references(getReferencePath(field.references.model, field.references.field)).onDelete(field.references.onDelete || "cascade"); + if (field.unique) col = col.unique(); + if (field.type === "date" && typeof field.defaultValue === "function" && (dbType === "postgres" || dbType === "mysql" || dbType === "mssql")) if (dbType === "mysql") col = col.defaultTo(sql`CURRENT_TIMESTAMP(3)`); + else col = col.defaultTo(sql`CURRENT_TIMESTAMP`); + return col; + }); + if (field.index) { + const builder = db2.schema.createIndex(`${table.table}_${fieldName}_${field.unique ? "uidx" : "idx"}`).on(table.table).columns([fieldName]); + toBeIndexed.push(field.unique ? builder.unique() : builder); + } + } + migrations.push(dbT); + } + if (toBeIndexed.length) for (const index of toBeIndexed) migrations.push(index); + async function runMigrations() { + for (const migration of migrations) await migration.execute(); + } + async function compileMigrations() { + return migrations.map((m) => m.compile().sql).join(";\n\n") + ";"; + } + return { + toBeCreated, + toBeAdded, + runMigrations, + compileMigrations + }; +} +var db_exports = /* @__PURE__ */ __export({ + convertFromDB: () => convertFromDB, + convertToDB: () => convertToDB, + createFieldAttribute: () => createFieldAttribute, + createInternalAdapter: () => createInternalAdapter, + getAdapter: () => getAdapter, + getBaseAdapter: () => getBaseAdapter, + getMigrations: () => getMigrations, + getSchema: () => getSchema, + getWithHooks: () => getWithHooks, + matchType: () => matchType, + mergeSchema: () => mergeSchema, + parseAccountInput: () => parseAccountInput, + parseAccountOutput: () => parseAccountOutput, + parseAdditionalUserInput: () => parseAdditionalUserInput, + parseInputData: () => parseInputData, + parseSessionInput: () => parseSessionInput, + parseSessionOutput: () => parseSessionOutput, + parseUserInput: () => parseUserInput, + parseUserOutput: () => parseUserOutput, + toZodSchema: () => toZodSchema +}); +__reExport(db_exports, import___better_auth_core_db); +async function signJWT(payload, secret, expiresIn = 3600) { + return await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(Math.floor(Date.now() / 1e3) + expiresIn).sign(new TextEncoder().encode(secret)); +} +async function verifyJWT(token, secret) { + try { + return (await jwtVerify(token, new TextEncoder().encode(secret))).payload; + } catch { + return null; + } +} +const info = new Uint8Array([ + 66, + 101, + 116, + 116, + 101, + 114, + 65, + 117, + 116, + 104, + 46, + 106, + 115, + 32, + 71, + 101, + 110, + 101, + 114, + 97, + 116, + 101, + 100, + 32, + 69, + 110, + 99, + 114, + 121, + 112, + 116, + 105, + 111, + 110, + 32, + 75, + 101, + 121 +]); +const now = () => Date.now() / 1e3 | 0; +const alg = "dir"; +const enc = "A256CBC-HS512"; +async function symmetricEncodeJWT(payload, secret, salt, expiresIn = 3600) { + const encryptionSecret = hkdf(sha256, new TextEncoder().encode(secret), new TextEncoder().encode(salt), info, 64); + const thumbprint = await calculateJwkThumbprint({ + kty: "oct", + k: base64url.encode(encryptionSecret) + }, "sha256"); + return await new EncryptJWT(payload).setProtectedHeader({ + alg, + enc, + kid: thumbprint + }).setIssuedAt().setExpirationTime(now() + expiresIn).setJti(crypto.randomUUID()).encrypt(encryptionSecret); +} +async function symmetricDecodeJWT(token, secret, salt) { + if (!token) return null; + try { + const { payload } = await jwtDecrypt(token, async ({ kid }) => { + const encryptionSecret = hkdf(sha256, new TextEncoder().encode(secret), new TextEncoder().encode(salt), info, 64); + if (kid === void 0) return encryptionSecret; + if (kid === await calculateJwkThumbprint({ + kty: "oct", + k: base64url.encode(encryptionSecret) + }, "sha256")) return encryptionSecret; + throw new Error("no matching decryption secret"); + }, { + clockTolerance: 15, + keyManagementAlgorithms: [alg], + contentEncryptionAlgorithms: [enc, "A256GCM"] + }); + return payload; + } catch { + return null; + } +} +function constantTimeEqual(a, b) { + if (typeof a === "string") a = new TextEncoder().encode(a); + if (typeof b === "string") b = new TextEncoder().encode(b); + const aBuffer = new Uint8Array(a); + const bBuffer = new Uint8Array(b); + let c = aBuffer.length ^ bBuffer.length; + const length = Math.max(aBuffer.length, bBuffer.length); + for (let i = 0; i < length; i++) c |= (i < aBuffer.length ? aBuffer[i] : 0) ^ (i < bBuffer.length ? bBuffer[i] : 0); + return c === 0; +} +const config = { + N: 16384, + r: 16, + p: 1, + dkLen: 64 +}; +async function generateKey(password, salt) { + return await scryptAsync(password.normalize("NFKC"), salt, { + N: config.N, + p: config.p, + r: config.r, + dkLen: config.dkLen, + maxmem: 128 * config.N * config.r * 2 + }); +} +const hashPassword = async (password) => { + const salt = hex.encode(crypto.getRandomValues(new Uint8Array(16))); + const key = await generateKey(password, salt); + return `${salt}:${hex.encode(key)}`; +}; +const verifyPassword$1 = async ({ hash, password }) => { + const [salt, key] = hash.split(":"); + if (!salt || !key) throw new BetterAuthError("Invalid password hash"); + return constantTimeEqual(await generateKey(password, salt), hexToBytes(key)); +}; +const generateRandomString = createRandomStringGenerator("a-z", "0-9", "A-Z", "-_"); +const symmetricEncrypt = async ({ key, data }) => { + const keyAsBytes = await createHash("SHA-256").digest(key); + const dataAsBytes = utf8ToBytes(data); + return bytesToHex(managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes)).encrypt(dataAsBytes)); +}; +const symmetricDecrypt = async ({ key, data }) => { + const keyAsBytes = await createHash("SHA-256").digest(key); + const dataAsBytes = hexToBytes$1(data); + const chacha = managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes)); + return new TextDecoder().decode(chacha.decrypt(dataAsBytes)); +}; +const ALLOWED_COOKIE_SIZE = 4096; +const ESTIMATED_EMPTY_COOKIE_SIZE = 200; +const CHUNK_SIZE = ALLOWED_COOKIE_SIZE - ESTIMATED_EMPTY_COOKIE_SIZE; +function parseCookiesFromContext(ctx) { + const cookieHeader = ctx.headers?.get("cookie"); + if (!cookieHeader) return {}; + const cookies = {}; + const pairs = cookieHeader.split("; "); + for (const pair of pairs) { + const [name, ...valueParts] = pair.split("="); + if (name && valueParts.length > 0) cookies[name] = valueParts.join("="); + } + return cookies; +} +function getChunkIndex(cookieName) { + const parts = cookieName.split("."); + const lastPart = parts[parts.length - 1]; + const index = parseInt(lastPart || "0", 10); + return isNaN(index) ? 0 : index; +} +function readExistingChunks(cookieName, ctx) { + const chunks = {}; + const cookies = parseCookiesFromContext(ctx); + for (const [name, value] of Object.entries(cookies)) if (name.startsWith(cookieName)) chunks[name] = value; + return chunks; +} +function joinChunks(chunks) { + return Object.keys(chunks).sort((a, b) => { + return getChunkIndex(a) - getChunkIndex(b); + }).map((key) => chunks[key]).join(""); +} +function chunkCookie(storeName, cookie, chunks, logger2) { + const chunkCount = Math.ceil(cookie.value.length / CHUNK_SIZE); + if (chunkCount === 1) { + chunks[cookie.name] = cookie.value; + return [cookie]; + } + const cookies = []; + for (let i = 0; i < chunkCount; i++) { + const name = `${cookie.name}.${i}`; + const start = i * CHUNK_SIZE; + const value = cookie.value.substring(start, start + CHUNK_SIZE); + cookies.push({ + ...cookie, + name, + value + }); + chunks[name] = value; + } + logger2.debug(`CHUNKING_${storeName.toUpperCase()}_COOKIE`, { + message: `${storeName} cookie exceeds allowed ${ALLOWED_COOKIE_SIZE} bytes.`, + emptyCookieSize: ESTIMATED_EMPTY_COOKIE_SIZE, + valueSize: cookie.value.length, + chunkCount, + chunks: cookies.map((c) => c.value.length + ESTIMATED_EMPTY_COOKIE_SIZE) + }); + return cookies; +} +function getCleanCookies(chunks, cookieOptions) { + const cleanedChunks = {}; + for (const name in chunks) cleanedChunks[name] = { + name, + value: "", + attributes: { + ...cookieOptions, + maxAge: 0 + } + }; + return cleanedChunks; +} +const storeFactory = (storeName) => (cookieName, cookieOptions, ctx) => { + const chunks = readExistingChunks(cookieName, ctx); + const logger2 = ctx.context.logger; + return { + getValue() { + return joinChunks(chunks); + }, + hasChunks() { + return Object.keys(chunks).length > 0; + }, + chunk(value, options) { + const cleanedChunks = getCleanCookies(chunks, cookieOptions); + for (const name in chunks) delete chunks[name]; + const cookies = cleanedChunks; + const chunked = chunkCookie(storeName, { + name: cookieName, + value, + attributes: { + ...cookieOptions, + ...options + } + }, chunks, logger2); + for (const chunk of chunked) cookies[chunk.name] = chunk; + return Object.values(cookies); + }, + clean() { + const cleanedChunks = getCleanCookies(chunks, cookieOptions); + for (const name in chunks) delete chunks[name]; + return Object.values(cleanedChunks); + }, + setCookies(cookies) { + for (const cookie of cookies) ctx.setCookie(cookie.name, cookie.value, cookie.attributes); + } + }; +}; +const createSessionStore = storeFactory("Session"); +const createAccountStore = storeFactory("Account"); +function getChunkedCookie(ctx, cookieName) { + const value = ctx.getCookie(cookieName); + if (value) return value; + const chunks = []; + const cookieHeader = ctx.headers?.get("cookie"); + if (!cookieHeader) return null; + const cookies = {}; + const pairs = cookieHeader.split("; "); + for (const pair of pairs) { + const [name, ...valueParts] = pair.split("="); + if (name && valueParts.length > 0) cookies[name] = valueParts.join("="); + } + for (const [name, val] of Object.entries(cookies)) if (name.startsWith(cookieName + ".")) { + const indexStr = name.split(".").at(-1); + const index = parseInt(indexStr || "0", 10); + if (!isNaN(index)) chunks.push({ + index, + value: val + }); + } + if (chunks.length > 0) { + chunks.sort((a, b) => a.index - b.index); + return chunks.map((c) => c.value).join(""); + } + return null; +} +async function setAccountCookie(c, accountData) { + const accountDataCookie = c.context.authCookies.accountData; + const options = { + maxAge: 300, + ...accountDataCookie.attributes + }; + const data = await symmetricEncodeJWT(accountData, c.context.secret, "better-auth-account", options.maxAge); + if (data.length > ALLOWED_COOKIE_SIZE) { + const accountStore = createAccountStore(accountDataCookie.name, options, c); + const cookies = accountStore.chunk(data, options); + accountStore.setCookies(cookies); + } else { + const accountStore = createAccountStore(accountDataCookie.name, options, c); + if (accountStore.hasChunks()) { + const cleanCookies = accountStore.clean(); + accountStore.setCookies(cleanCookies); + } + c.setCookie(accountDataCookie.name, data, options); + } +} +async function getAccountCookie(c) { + const accountCookie = getChunkedCookie(c, c.context.authCookies.accountData.name); + if (accountCookie) { + const accountData = safeJSONParse(await symmetricDecodeJWT(accountCookie, c.context.secret, "better-auth-account")); + if (accountData) return accountData; + } + return null; +} +const getSessionQuerySchema = z.optional(z.object({ + disableCookieCache: z.coerce.boolean().meta({ description: "Disable cookie cache and fetch session from database" }).optional(), + disableRefresh: z.coerce.boolean().meta({ description: "Disable session refresh. Useful for checking session status, without updating the session" }).optional() +})); +function isPromise(obj) { + return !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function"; +} +const SEC = 1e3; +const MIN = SEC * 60; +const HOUR = MIN * 60; +const DAY = HOUR * 24; +const WEEK = DAY * 7; +const MONTH = DAY * 30; +const YEAR = DAY * 365.25; +const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)(?: (ago|from now))?$/i; +function parse(value) { + const match = REGEX.exec(value); + if (!match || match[4] && match[1]) throw new TypeError(`Invalid time string format: "${value}". Use formats like "7d", "30m", "1 hour", etc.`); + const n = parseFloat(match[2]); + const unit = match[3].toLowerCase(); + let result; + switch (unit) { + case "years": + case "year": + case "yrs": + case "yr": + case "y": + result = n * YEAR; + break; + case "months": + case "month": + case "mo": + result = n * MONTH; + break; + case "weeks": + case "week": + case "w": + result = n * WEEK; + break; + case "days": + case "day": + case "d": + result = n * DAY; + break; + case "hours": + case "hour": + case "hrs": + case "hr": + case "h": + result = n * HOUR; + break; + case "minutes": + case "minute": + case "mins": + case "min": + case "m": + result = n * MIN; + break; + case "seconds": + case "second": + case "secs": + case "sec": + case "s": + result = n * SEC; + break; + default: + throw new TypeError(`Unknown time unit: "${unit}"`); + } + if (match[1] === "-" || match[4] === "ago") return -result; + return result; +} +function ms(value) { + return parse(value); +} +function sec(value) { + return Math.round(parse(value) / 1e3); +} +const SECURE_COOKIE_PREFIX = "__Secure-"; +function splitSetCookieHeader(setCookie) { + if (!setCookie) return []; + const result = []; + let current = ""; + let i = 0; + while (i < setCookie.length) { + const c = setCookie[i]; + if (c === ",") { + const lower = current.toLowerCase(); + if (lower.includes("expires=") && !lower.includes("gmt")) { + current += c; + i++; + } else { + const trimmed$1 = current.trim(); + if (trimmed$1) result.push(trimmed$1); + current = ""; + i++; + if (i < setCookie.length && setCookie[i] === " ") i++; + } + continue; + } + current += c; + i++; + } + const trimmed = current.trim(); + if (trimmed) result.push(trimmed); + return result; +} +function parseSetCookieHeader(setCookie) { + const cookies = /* @__PURE__ */ new Map(); + splitSetCookieHeader(setCookie).forEach((cookieString) => { + const [nameValue, ...attributes] = cookieString.split(";").map((part) => part.trim()); + const [name, ...valueParts] = (nameValue || "").split("="); + const value = valueParts.join("="); + if (!name || value === void 0) return; + const attrObj = { value }; + attributes.forEach((attribute) => { + const [attrName, ...attrValueParts] = attribute.split("="); + const attrValue = attrValueParts.join("="); + const normalizedAttrName = attrName.trim().toLowerCase(); + switch (normalizedAttrName) { + case "max-age": + attrObj["max-age"] = attrValue ? parseInt(attrValue.trim(), 10) : void 0; + break; + case "expires": + attrObj.expires = attrValue ? new Date(attrValue.trim()) : void 0; + break; + case "domain": + attrObj.domain = attrValue ? attrValue.trim() : void 0; + break; + case "path": + attrObj.path = attrValue ? attrValue.trim() : void 0; + break; + case "secure": + attrObj.secure = true; + break; + case "httponly": + attrObj.httponly = true; + break; + case "samesite": + attrObj.samesite = attrValue ? attrValue.trim().toLowerCase() : void 0; + break; + default: + attrObj[normalizedAttrName] = attrValue ? attrValue.trim() : true; + break; + } + }); + cookies.set(name, attrObj); + }); + return cookies; +} +function createCookieGetter(options) { + const secureCookiePrefix = (options.advanced?.useSecureCookies !== void 0 ? options.advanced?.useSecureCookies : options.baseURL ? options.baseURL.startsWith("https://") ? true : false : isProduction) ? SECURE_COOKIE_PREFIX : ""; + const crossSubdomainEnabled = !!options.advanced?.crossSubDomainCookies?.enabled; + const domain = crossSubdomainEnabled ? options.advanced?.crossSubDomainCookies?.domain || (options.baseURL ? new URL(options.baseURL).hostname : void 0) : void 0; + if (crossSubdomainEnabled && !domain) throw new BetterAuthError("baseURL is required when crossSubdomainCookies are enabled"); + function createCookie(cookieName, overrideAttributes = {}) { + const prefix = options.advanced?.cookiePrefix || "better-auth"; + const name = options.advanced?.cookies?.[cookieName]?.name || `${prefix}.${cookieName}`; + const attributes = options.advanced?.cookies?.[cookieName]?.attributes; + return { + name: `${secureCookiePrefix}${name}`, + attributes: { + secure: !!secureCookiePrefix, + sameSite: "lax", + path: "/", + httpOnly: true, + ...crossSubdomainEnabled ? { domain } : {}, + ...options.advanced?.defaultCookieAttributes, + ...overrideAttributes, + ...attributes + } + }; + } + return createCookie; +} +function getCookies(options) { + const createCookie = createCookieGetter(options); + const sessionToken = createCookie("session_token", { maxAge: options.session?.expiresIn || sec("7d") }); + const sessionData = createCookie("session_data", { maxAge: options.session?.cookieCache?.maxAge || 300 }); + const accountData = createCookie("account_data", { maxAge: options.session?.cookieCache?.maxAge || 300 }); + const dontRememberToken = createCookie("dont_remember"); + return { + sessionToken: { + name: sessionToken.name, + attributes: sessionToken.attributes + }, + sessionData: { + name: sessionData.name, + attributes: sessionData.attributes + }, + dontRememberToken: { + name: dontRememberToken.name, + attributes: dontRememberToken.attributes + }, + accountData: { + name: accountData.name, + attributes: accountData.attributes + } + }; +} +async function setCookieCache(ctx, session, dontRememberMe) { + if (!ctx.context.options.session?.cookieCache?.enabled) return; + const filteredSession = filterOutputFields(session.session, ctx.context.options.session?.additionalFields); + const filteredUser = parseUserOutput(ctx.context.options, session.user); + const versionConfig = ctx.context.options.session?.cookieCache?.version; + let version = "1"; + if (versionConfig) { + if (typeof versionConfig === "string") version = versionConfig; + else if (typeof versionConfig === "function") { + const result = versionConfig(session.session, session.user); + version = isPromise(result) ? await result : result; + } + } + const sessionData = { + session: filteredSession, + user: filteredUser, + updatedAt: Date.now(), + version + }; + const options = { + ...ctx.context.authCookies.sessionData.attributes, + maxAge: dontRememberMe ? void 0 : ctx.context.authCookies.sessionData.attributes.maxAge + }; + const expiresAtDate = getDate(options.maxAge || 60, "sec").getTime(); + const strategy = ctx.context.options.session?.cookieCache?.strategy || "compact"; + let data; + if (strategy === "jwe") data = await symmetricEncodeJWT(sessionData, ctx.context.secret, "better-auth-session", options.maxAge || 300); + else if (strategy === "jwt") data = await signJWT(sessionData, ctx.context.secret, options.maxAge || 300); + else data = base64Url.encode(JSON.stringify({ + session: sessionData, + expiresAt: expiresAtDate, + signature: await createHMAC("SHA-256", "base64urlnopad").sign(ctx.context.secret, JSON.stringify({ + ...sessionData, + expiresAt: expiresAtDate + })) + }), { padding: false }); + if (data.length > 4093) { + const sessionStore = createSessionStore(ctx.context.authCookies.sessionData.name, options, ctx); + const cookies = sessionStore.chunk(data, options); + sessionStore.setCookies(cookies); + } else { + const sessionStore = createSessionStore(ctx.context.authCookies.sessionData.name, options, ctx); + if (sessionStore.hasChunks()) { + const cleanCookies = sessionStore.clean(); + sessionStore.setCookies(cleanCookies); + } + ctx.setCookie(ctx.context.authCookies.sessionData.name, data, options); + } + if (ctx.context.options.account?.storeAccountCookie) { + const accountData = await getAccountCookie(ctx); + if (accountData) await setAccountCookie(ctx, accountData); + } +} +async function setSessionCookie(ctx, session, dontRememberMe, overrides) { + const dontRememberMeCookie = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret); + dontRememberMe = dontRememberMe !== void 0 ? dontRememberMe : !!dontRememberMeCookie; + const options = ctx.context.authCookies.sessionToken.attributes; + const maxAge = dontRememberMe ? void 0 : ctx.context.sessionConfig.expiresIn; + await ctx.setSignedCookie(ctx.context.authCookies.sessionToken.name, session.session.token, ctx.context.secret, { + ...options, + maxAge, + ...overrides + }); + if (dontRememberMe) await ctx.setSignedCookie(ctx.context.authCookies.dontRememberToken.name, "true", ctx.context.secret, ctx.context.authCookies.dontRememberToken.attributes); + await setCookieCache(ctx, session, dontRememberMe); + ctx.context.setNewSession(session); +} +function expireCookie(ctx, cookie) { + ctx.setCookie(cookie.name, "", { + ...cookie.attributes, + maxAge: 0 + }); +} +function deleteSessionCookie(ctx, skipDontRememberMe) { + expireCookie(ctx, ctx.context.authCookies.sessionToken); + expireCookie(ctx, ctx.context.authCookies.sessionData); + if (ctx.context.options.account?.storeAccountCookie) { + expireCookie(ctx, ctx.context.authCookies.accountData); + const accountStore = createAccountStore(ctx.context.authCookies.accountData.name, ctx.context.authCookies.accountData.attributes, ctx); + const cleanCookies$1 = accountStore.clean(); + accountStore.setCookies(cleanCookies$1); + } + if (ctx.context.oauthConfig.storeStateStrategy === "cookie") expireCookie(ctx, ctx.context.createAuthCookie("oauth_state")); + const sessionStore = createSessionStore(ctx.context.authCookies.sessionData.name, ctx.context.authCookies.sessionData.attributes, ctx); + const cleanCookies = sessionStore.clean(); + sessionStore.setCookies(cleanCookies); + expireCookie(ctx, ctx.context.authCookies.dontRememberToken); +} +const getSession = () => createAuthEndpoint("/get-session", { + method: "GET", + operationId: "getSession", + query: getSessionQuerySchema, + requireHeaders: true, + metadata: { openapi: { + operationId: "getSession", + description: "Get the current session", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + nullable: true, + properties: { + session: { $ref: "#/components/schemas/Session" }, + user: { $ref: "#/components/schemas/User" } + }, + required: ["session", "user"] + } } } + } } + } } +}, async (ctx) => { + try { + const sessionCookieToken = await ctx.getSignedCookie(ctx.context.authCookies.sessionToken.name, ctx.context.secret); + if (!sessionCookieToken) return null; + const sessionDataCookie = getChunkedCookie(ctx, ctx.context.authCookies.sessionData.name); + let sessionDataPayload = null; + if (sessionDataCookie) { + const strategy = ctx.context.options.session?.cookieCache?.strategy || "compact"; + if (strategy === "jwe") { + const payload = await symmetricDecodeJWT(sessionDataCookie, ctx.context.secret, "better-auth-session"); + if (payload && payload.session && payload.user) sessionDataPayload = { + session: { + session: payload.session, + user: payload.user, + updatedAt: payload.updatedAt, + version: payload.version + }, + expiresAt: payload.exp ? payload.exp * 1e3 : Date.now() + }; + else { + expireCookie(ctx, ctx.context.authCookies.sessionData); + return ctx.json(null); + } + } else if (strategy === "jwt") { + const payload = await verifyJWT(sessionDataCookie, ctx.context.secret); + if (payload && payload.session && payload.user) sessionDataPayload = { + session: { + session: payload.session, + user: payload.user, + updatedAt: payload.updatedAt, + version: payload.version + }, + expiresAt: payload.exp ? payload.exp * 1e3 : Date.now() + }; + else { + expireCookie(ctx, ctx.context.authCookies.sessionData); + return ctx.json(null); + } + } else { + const parsed = safeJSONParse(binary.decode(base64Url.decode(sessionDataCookie))); + if (parsed) if (await createHMAC("SHA-256", "base64urlnopad").verify(ctx.context.secret, JSON.stringify({ + ...parsed.session, + expiresAt: parsed.expiresAt + }), parsed.signature)) sessionDataPayload = parsed; + else { + expireCookie(ctx, ctx.context.authCookies.sessionData); + return ctx.json(null); + } + } + } + const dontRememberMe = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret); + if (sessionDataPayload?.session && ctx.context.options.session?.cookieCache?.enabled && !ctx.query?.disableCookieCache) { + const session$1 = sessionDataPayload.session; + const versionConfig = ctx.context.options.session?.cookieCache?.version; + let expectedVersion = "1"; + if (versionConfig) { + if (typeof versionConfig === "string") expectedVersion = versionConfig; + else if (typeof versionConfig === "function") { + const result = versionConfig(session$1.session, session$1.user); + expectedVersion = result instanceof Promise ? await result : result; + } + } + if ((session$1.version || "1") !== expectedVersion) expireCookie(ctx, ctx.context.authCookies.sessionData); + else { + const cachedSessionExpiresAt = new Date(session$1.session.expiresAt); + if (sessionDataPayload.expiresAt < Date.now() || cachedSessionExpiresAt < /* @__PURE__ */ new Date()) expireCookie(ctx, ctx.context.authCookies.sessionData); + else { + const cookieRefreshCache = ctx.context.sessionConfig.cookieRefreshCache; + if (cookieRefreshCache === false) { + ctx.context.session = session$1; + const parsedSession$2 = parseSessionOutput(ctx.context.options, { + ...session$1.session, + expiresAt: new Date(session$1.session.expiresAt), + createdAt: new Date(session$1.session.createdAt), + updatedAt: new Date(session$1.session.updatedAt) + }); + const parsedUser$2 = parseUserOutput(ctx.context.options, { + ...session$1.user, + createdAt: new Date(session$1.user.createdAt), + updatedAt: new Date(session$1.user.updatedAt) + }); + return ctx.json({ + session: parsedSession$2, + user: parsedUser$2 + }); + } + if (sessionDataPayload.expiresAt - Date.now() < cookieRefreshCache.updateAge * 1e3) { + const newExpiresAt = getDate(ctx.context.options.session?.cookieCache?.maxAge || 300, "sec"); + const refreshedSession = { + session: { + ...session$1.session, + expiresAt: newExpiresAt + }, + user: session$1.user, + updatedAt: Date.now() + }; + await setCookieCache(ctx, refreshedSession, false); + const parsedRefreshedSession = parseSessionOutput(ctx.context.options, { + ...refreshedSession.session, + expiresAt: new Date(refreshedSession.session.expiresAt), + createdAt: new Date(refreshedSession.session.createdAt), + updatedAt: new Date(refreshedSession.session.updatedAt) + }); + const parsedRefreshedUser = parseUserOutput(ctx.context.options, { + ...refreshedSession.user, + createdAt: new Date(refreshedSession.user.createdAt), + updatedAt: new Date(refreshedSession.user.updatedAt) + }); + ctx.context.session = { + session: parsedRefreshedSession, + user: parsedRefreshedUser + }; + return ctx.json({ + session: parsedRefreshedSession, + user: parsedRefreshedUser + }); + } + const parsedSession$1 = parseSessionOutput(ctx.context.options, { + ...session$1.session, + expiresAt: new Date(session$1.session.expiresAt), + createdAt: new Date(session$1.session.createdAt), + updatedAt: new Date(session$1.session.updatedAt) + }); + const parsedUser$1 = parseUserOutput(ctx.context.options, { + ...session$1.user, + createdAt: new Date(session$1.user.createdAt), + updatedAt: new Date(session$1.user.updatedAt) + }); + ctx.context.session = { + session: parsedSession$1, + user: parsedUser$1 + }; + return ctx.json({ + session: parsedSession$1, + user: parsedUser$1 + }); + } + } + } + const session = await ctx.context.internalAdapter.findSession(sessionCookieToken); + ctx.context.session = session; + if (!session || session.session.expiresAt < /* @__PURE__ */ new Date()) { + deleteSessionCookie(ctx); + if (session) + await ctx.context.internalAdapter.deleteSession(session.session.token); + return ctx.json(null); + } + if (dontRememberMe || ctx.query?.disableRefresh) { + const parsedSession$1 = parseSessionOutput(ctx.context.options, session.session); + const parsedUser$1 = parseUserOutput(ctx.context.options, session.user); + return ctx.json({ + session: parsedSession$1, + user: parsedUser$1 + }); + } + const expiresIn = ctx.context.sessionConfig.expiresIn; + const updateAge = ctx.context.sessionConfig.updateAge; + if (session.session.expiresAt.valueOf() - expiresIn * 1e3 + updateAge * 1e3 <= Date.now() && (!ctx.query?.disableRefresh || !ctx.context.options.session?.disableSessionRefresh)) { + const updatedSession = await ctx.context.internalAdapter.updateSession(session.session.token, { + expiresAt: getDate(ctx.context.sessionConfig.expiresIn, "sec"), + updatedAt: /* @__PURE__ */ new Date() + }); + if (!updatedSession) { + deleteSessionCookie(ctx); + return ctx.json(null, { status: 401 }); + } + const maxAge = (updatedSession.expiresAt.valueOf() - Date.now()) / 1e3; + await setSessionCookie(ctx, { + session: updatedSession, + user: session.user + }, false, { maxAge }); + const parsedUpdatedSession = parseSessionOutput(ctx.context.options, updatedSession); + const parsedUser$1 = parseUserOutput(ctx.context.options, session.user); + return ctx.json({ + session: parsedUpdatedSession, + user: parsedUser$1 + }); + } + await setCookieCache(ctx, session, !!dontRememberMe); + const parsedSession = parseSessionOutput(ctx.context.options, session.session); + const parsedUser = parseUserOutput(ctx.context.options, session.user); + return ctx.json({ + session: parsedSession, + user: parsedUser + }); + } catch (error2) { + ctx.context.logger.error("INTERNAL_SERVER_ERROR", error2); + throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_GET_SESSION }); + } +}); +const getSessionFromCtx = async (ctx, config2) => { + if (ctx.context.session) return ctx.context.session; + const session = await getSession()({ + ...ctx, + asResponse: false, + headers: ctx.headers, + returnHeaders: false, + returnStatus: false, + query: { + ...config2, + ...ctx.query + } + }).catch((e) => { + return null; + }); + ctx.context.session = session; + return session; +}; +const sessionMiddleware = createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session?.session) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +const sensitiveSessionMiddleware = createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx, { disableCookieCache: true }); + if (!session?.session) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session?.session && (ctx.request || ctx.headers)) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +const freshSessionMiddleware = createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session?.session) throw new APIError("UNAUTHORIZED"); + if (ctx.context.sessionConfig.freshAge === 0) return { session }; + const freshAge = ctx.context.sessionConfig.freshAge; + const lastUpdated = new Date(session.session.updatedAt || session.session.createdAt).getTime(); + if (!(Date.now() - lastUpdated < freshAge * 1e3)) throw new APIError("FORBIDDEN", { message: "Session is not fresh" }); + return { session }; +}); +const listSessions = () => createAuthEndpoint("/list-sessions", { + method: "GET", + operationId: "listUserSessions", + use: [sessionMiddleware], + requireHeaders: true, + metadata: { openapi: { + operationId: "listUserSessions", + description: "List all active sessions for the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "array", + items: { $ref: "#/components/schemas/Session" } + } } } + } } + } } +}, async (ctx) => { + try { + const activeSessions = (await ctx.context.internalAdapter.listSessions(ctx.context.session.user.id)).filter((session) => { + return session.expiresAt > /* @__PURE__ */ new Date(); + }); + return ctx.json(activeSessions.map((session) => parseSessionOutput(ctx.context.options, session))); + } catch (e) { + ctx.context.logger.error(e); + throw ctx.error("INTERNAL_SERVER_ERROR"); + } +}); +const revokeSession = createAuthEndpoint("/revoke-session", { + method: "POST", + body: z.object({ token: z.string().meta({ description: "The token to revoke" }) }), + use: [sensitiveSessionMiddleware], + requireHeaders: true, + metadata: { openapi: { + description: "Revoke a single session", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { token: { + type: "string", + description: "The token to revoke" + } }, + required: ["token"] + } } } }, + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if the session was revoked successfully" + } }, + required: ["status"] + } } } + } } + } } +}, async (ctx) => { + const token = ctx.body.token; + if ((await ctx.context.internalAdapter.findSession(token))?.session.userId === ctx.context.session.user.id) try { + await ctx.context.internalAdapter.deleteSession(token); + } catch (error2) { + ctx.context.logger.error(error2 && typeof error2 === "object" && "name" in error2 ? error2.name : "", error2); + throw new APIError("INTERNAL_SERVER_ERROR"); + } + return ctx.json({ status: true }); +}); +const revokeSessions = createAuthEndpoint("/revoke-sessions", { + method: "POST", + use: [sensitiveSessionMiddleware], + requireHeaders: true, + metadata: { openapi: { + description: "Revoke all sessions for the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if all sessions were revoked successfully" + } }, + required: ["status"] + } } } + } } + } } +}, async (ctx) => { + try { + await ctx.context.internalAdapter.deleteSessions(ctx.context.session.user.id); + } catch (error2) { + ctx.context.logger.error(error2 && typeof error2 === "object" && "name" in error2 ? error2.name : "", error2); + throw new APIError("INTERNAL_SERVER_ERROR"); + } + return ctx.json({ status: true }); +}); +const revokeOtherSessions = createAuthEndpoint("/revoke-other-sessions", { + method: "POST", + requireHeaders: true, + use: [sensitiveSessionMiddleware], + metadata: { openapi: { + description: "Revoke all other sessions for the user except the current one", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if all other sessions were revoked successfully" + } }, + required: ["status"] + } } } + } } + } } +}, async (ctx) => { + const session = ctx.context.session; + if (!session.user) throw new APIError("UNAUTHORIZED"); + const otherSessions = (await ctx.context.internalAdapter.listSessions(session.user.id)).filter((session$1) => { + return session$1.expiresAt > /* @__PURE__ */ new Date(); + }).filter((session$1) => session$1.token !== ctx.context.session.session.token); + await Promise.all(otherSessions.map((session$1) => ctx.context.internalAdapter.deleteSession(session$1.token))); + return ctx.json({ status: true }); +}); +const stateDataSchema = z.looseObject({ + callbackURL: z.string(), + codeVerifier: z.string(), + errorURL: z.string().optional(), + newUserURL: z.string().optional(), + expiresAt: z.number(), + link: z.object({ + email: z.string(), + userId: z.coerce.string() + }).optional(), + requestSignUp: z.boolean().optional() +}); +var StateError = class extends BetterAuthError { + code; + details; + constructor(message, options) { + super(message, options); + this.code = options.code; + this.details = options.details; + } +}; +async function generateGenericState(c, stateData, settings) { + const state = generateRandomString(32); + if (c.context.oauthConfig.storeStateStrategy === "cookie") { + const encryptedData = await symmetricEncrypt({ + key: c.context.secret, + data: JSON.stringify(stateData) + }); + const stateCookie$1 = c.context.createAuthCookie("oauth_state", { maxAge: 600 }); + c.setCookie(stateCookie$1.name, encryptedData, stateCookie$1.attributes); + return { + state, + codeVerifier: stateData.codeVerifier + }; + } + const stateCookie = c.context.createAuthCookie("state", { maxAge: 300 }); + await c.setSignedCookie(stateCookie.name, state, c.context.secret, stateCookie.attributes); + const expiresAt = /* @__PURE__ */ new Date(); + expiresAt.setMinutes(expiresAt.getMinutes() + 10); + const verification = await c.context.internalAdapter.createVerificationValue({ + value: JSON.stringify(stateData), + identifier: state, + expiresAt + }); + if (!verification) throw new StateError("Unable to create verification. Make sure the database adapter is properly working and there is a verification table in the database", { code: "state_generation_error" }); + return { + state: verification.identifier, + codeVerifier: stateData.codeVerifier + }; +} +async function parseGenericState(c, state, settings) { + const storeStateStrategy = c.context.oauthConfig.storeStateStrategy; + let parsedData; + if (storeStateStrategy === "cookie") { + const stateCookie = c.context.createAuthCookie("oauth_state"); + const encryptedData = c.getCookie(stateCookie.name); + if (!encryptedData) throw new StateError("State mismatch: auth state cookie not found", { + code: "state_mismatch", + details: { state } + }); + try { + const decryptedData = await symmetricDecrypt({ + key: c.context.secret, + data: encryptedData + }); + parsedData = stateDataSchema.parse(JSON.parse(decryptedData)); + } catch (error2) { + throw new StateError("State invalid: Failed to decrypt or parse auth state", { + code: "state_invalid", + details: { state }, + cause: error2 + }); + } + expireCookie(c, stateCookie); + } else { + const data = await c.context.internalAdapter.findVerificationValue(state); + if (!data) throw new StateError("State mismatch: verification not found", { + code: "state_mismatch", + details: { state } + }); + parsedData = stateDataSchema.parse(JSON.parse(data.value)); + const stateCookie = c.context.createAuthCookie("state"); + const stateCookieValue = await c.getSignedCookie(stateCookie.name, c.context.secret); + if (!c.context.oauthConfig.skipStateCookieCheck && (!stateCookieValue || stateCookieValue !== state)) throw new StateError("State mismatch: State not persisted correctly", { + code: "state_security_mismatch", + details: { state } + }); + expireCookie(c, stateCookie); + await c.context.internalAdapter.deleteVerificationValue(data.id); + } + if (parsedData.expiresAt < Date.now()) throw new StateError("Invalid state: request expired", { + code: "state_mismatch", + details: { expiresAt: parsedData.expiresAt } + }); + return parsedData; +} +async function generateState(c, link, additionalData) { + const callbackURL = c.body?.callbackURL || c.context.options.baseURL; + if (!callbackURL) throw new APIError("BAD_REQUEST", { message: "callbackURL is required" }); + const codeVerifier = generateRandomString(128); + const stateData = { + ...additionalData ? additionalData : {}, + callbackURL, + codeVerifier, + errorURL: c.body?.errorCallbackURL, + newUserURL: c.body?.newUserCallbackURL, + link, + expiresAt: Date.now() + 600 * 1e3, + requestSignUp: c.body?.requestSignUp + }; + await setOAuthState(stateData); + try { + return generateGenericState(c, stateData); + } catch (error2) { + c.context.logger.error("Failed to create verification", error2); + throw new APIError("INTERNAL_SERVER_ERROR", { + message: "Unable to create verification", + cause: error2 + }); + } +} +async function parseState(c) { + const state = c.query.state || c.body.state; + const errorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`; + let parsedData; + try { + parsedData = await parseGenericState(c, state); + } catch (error2) { + c.context.logger.error("Failed to parse state", error2); + if (error2 instanceof StateError && error2.code === "state_security_mismatch") throw c.redirect(`${errorURL}?error=state_mismatch`); + throw c.redirect(`${errorURL}?error=please_restart_the_process`); + } + if (!parsedData.errorURL) parsedData.errorURL = errorURL; + if (parsedData) await setOAuthState(parsedData); + return parsedData; +} +function decryptOAuthToken(token, ctx) { + if (!token) return token; + if (ctx.options.account?.encryptOAuthTokens) return symmetricDecrypt({ + key: ctx.secret, + data: token + }); + return token; +} +function setTokenUtil(token, ctx) { + if (ctx.options.account?.encryptOAuthTokens && token) return symmetricEncrypt({ + key: ctx.secret, + data: token + }); + return token; +} +const listUserAccounts = createAuthEndpoint("/list-accounts", { + method: "GET", + use: [sessionMiddleware], + metadata: { openapi: { + operationId: "listUserAccounts", + description: "List all accounts linked to the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "array", + items: { + type: "object", + properties: { + id: { type: "string" }, + providerId: { type: "string" }, + createdAt: { + type: "string", + format: "date-time" + }, + updatedAt: { + type: "string", + format: "date-time" + }, + accountId: { type: "string" }, + userId: { type: "string" }, + scopes: { + type: "array", + items: { type: "string" } + } + }, + required: [ + "id", + "providerId", + "createdAt", + "updatedAt", + "accountId", + "userId", + "scopes" + ] + } + } } } + } } + } } +}, async (c) => { + const session = c.context.session; + const accounts = await c.context.internalAdapter.findAccounts(session.user.id); + return c.json(accounts.map((a) => { + const { scope, ...parsed } = parseAccountOutput(c.context.options, a); + return { + ...parsed, + scopes: scope?.split(",") || [] + }; + })); +}); +const linkSocialAccount = createAuthEndpoint("/link-social", { + method: "POST", + requireHeaders: true, + body: z.object({ + callbackURL: z.string().meta({ description: "The URL to redirect to after the user has signed in" }).optional(), + provider: SocialProviderListEnum, + idToken: z.object({ + token: z.string(), + nonce: z.string().optional(), + accessToken: z.string().optional(), + refreshToken: z.string().optional(), + scopes: z.array(z.string()).optional() + }).optional(), + requestSignUp: z.boolean().optional(), + scopes: z.array(z.string()).meta({ description: "Additional scopes to request from the provider" }).optional(), + errorCallbackURL: z.string().meta({ description: "The URL to redirect to if there is an error during the link process" }).optional(), + disableRedirect: z.boolean().meta({ description: "Disable automatic redirection to the provider. Useful for handling the redirection yourself" }).optional(), + additionalData: z.record(z.string(), z.any()).optional() + }), + use: [sessionMiddleware], + metadata: { openapi: { + description: "Link a social account to the user", + operationId: "linkSocialAccount", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + url: { + type: "string", + description: "The authorization URL to redirect the user to" + }, + redirect: { + type: "boolean", + description: "Indicates if the user should be redirected to the authorization URL" + }, + status: { type: "boolean" } + }, + required: ["redirect"] + } } } + } } + } } +}, async (c) => { + const session = c.context.session; + const provider = c.context.socialProviders.find((p) => p.id === c.body.provider); + if (!provider) { + c.context.logger.error("Provider not found. Make sure to add the provider in your auth config", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.PROVIDER_NOT_FOUND }); + } + if (c.body.idToken) { + if (!provider.verifyIdToken) { + c.context.logger.error("Provider does not support id token verification", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.ID_TOKEN_NOT_SUPPORTED }); + } + const { token, nonce } = c.body.idToken; + if (!await provider.verifyIdToken(token, nonce)) { + c.context.logger.error("Invalid id token", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + } + const linkingUserInfo = await provider.getUserInfo({ + idToken: token, + accessToken: c.body.idToken.accessToken, + refreshToken: c.body.idToken.refreshToken + }); + if (!linkingUserInfo || !linkingUserInfo?.user) { + c.context.logger.error("Failed to get user info", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO }); + } + const linkingUserId = String(linkingUserInfo.user.id); + if (!linkingUserInfo.user.email) { + c.context.logger.error("User email not found", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.USER_EMAIL_NOT_FOUND }); + } + if ((await c.context.internalAdapter.findAccounts(session.user.id)).find((a) => a.providerId === provider.id && a.accountId === linkingUserId)) return c.json({ + url: "", + status: true, + redirect: false + }); + if (!c.context.options.account?.accountLinking?.trustedProviders?.includes(provider.id) && !linkingUserInfo.user.emailVerified || c.context.options.account?.accountLinking?.enabled === false) throw new APIError("UNAUTHORIZED", { message: "Account not linked - linking not allowed" }); + if (linkingUserInfo.user.email?.toLowerCase() !== session.user.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) throw new APIError("UNAUTHORIZED", { message: "Account not linked - different emails not allowed" }); + try { + await c.context.internalAdapter.createAccount({ + userId: session.user.id, + providerId: provider.id, + accountId: linkingUserId, + accessToken: c.body.idToken.accessToken, + idToken: token, + refreshToken: c.body.idToken.refreshToken, + scope: c.body.idToken.scopes?.join(",") + }); + } catch { + throw new APIError("EXPECTATION_FAILED", { message: "Account not linked - unable to create account" }); + } + if (c.context.options.account?.accountLinking?.updateUserInfoOnLink === true) try { + await c.context.internalAdapter.updateUser(session.user.id, { + name: linkingUserInfo.user?.name, + image: linkingUserInfo.user?.image + }); + } catch (e) { + console.warn("Could not update user - " + e.toString()); + } + return c.json({ + url: "", + status: true, + redirect: false + }); + } + const state = await generateState(c, { + userId: session.user.id, + email: session.user.email + }, c.body.additionalData); + const url = await provider.createAuthorizationURL({ + state: state.state, + codeVerifier: state.codeVerifier, + redirectURI: `${c.context.baseURL}/callback/${provider.id}`, + scopes: c.body.scopes + }); + if (!c.body.disableRedirect) c.setHeader("Location", url.toString()); + return c.json({ + url: url.toString(), + redirect: !c.body.disableRedirect + }); +}); +const unlinkAccount = createAuthEndpoint("/unlink-account", { + method: "POST", + body: z.object({ + providerId: z.string(), + accountId: z.string().optional() + }), + use: [freshSessionMiddleware], + metadata: { openapi: { + description: "Unlink an account", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const { providerId, accountId } = ctx.body; + const accounts = await ctx.context.internalAdapter.findAccounts(ctx.context.session.user.id); + if (accounts.length === 1 && !ctx.context.options.account?.accountLinking?.allowUnlinkingAll) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_UNLINK_LAST_ACCOUNT }); + const accountExist = accounts.find((account) => accountId ? account.accountId === accountId && account.providerId === providerId : account.providerId === providerId); + if (!accountExist) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.ACCOUNT_NOT_FOUND }); + await ctx.context.internalAdapter.deleteAccount(accountExist.id); + return ctx.json({ status: true }); +}); +const getAccessToken = createAuthEndpoint("/get-access-token", { + method: "POST", + body: z.object({ + providerId: z.string().meta({ description: "The provider ID for the OAuth provider" }), + accountId: z.string().meta({ description: "The account ID associated with the refresh token" }).optional(), + userId: z.string().meta({ description: "The user ID associated with the account" }).optional() + }), + metadata: { openapi: { + description: "Get a valid access token, doing a refresh if needed", + responses: { + 200: { + description: "A Valid access token", + content: { "application/json": { schema: { + type: "object", + properties: { + tokenType: { type: "string" }, + idToken: { type: "string" }, + accessToken: { type: "string" }, + accessTokenExpiresAt: { + type: "string", + format: "date-time" + } + } + } } } + }, + 400: { description: "Invalid refresh token or provider configuration" } + } + } } +}, async (ctx) => { + const { providerId, accountId, userId } = ctx.body || {}; + const req = ctx.request; + const session = await getSessionFromCtx(ctx); + if (req && !session) throw ctx.error("UNAUTHORIZED"); + const resolvedUserId = session?.user?.id || userId; + if (!resolvedUserId) throw ctx.error("UNAUTHORIZED"); + if (!ctx.context.socialProviders.find((p) => p.id === providerId)) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} is not supported.` }); + const accountData = await getAccountCookie(ctx); + let account = void 0; + if (accountData && providerId === accountData.providerId && (!accountId || accountData.id === accountId)) account = accountData; + else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.id === accountId && acc.providerId === providerId : acc.providerId === providerId); + if (!account) throw new APIError("BAD_REQUEST", { message: "Account not found" }); + const provider = ctx.context.socialProviders.find((p) => p.id === providerId); + if (!provider) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} not found.` }); + try { + let newTokens = null; + const accessTokenExpired = account.accessTokenExpiresAt && new Date(account.accessTokenExpiresAt).getTime() - Date.now() < 5e3; + if (account.refreshToken && accessTokenExpired && provider.refreshAccessToken) { + const refreshToken$1 = await decryptOAuthToken(account.refreshToken, ctx.context); + newTokens = await provider.refreshAccessToken(refreshToken$1); + const updatedData = { + accessToken: await setTokenUtil(newTokens.accessToken, ctx.context), + accessTokenExpiresAt: newTokens.accessTokenExpiresAt, + refreshToken: await setTokenUtil(newTokens.refreshToken, ctx.context), + refreshTokenExpiresAt: newTokens.refreshTokenExpiresAt + }; + let updatedAccount = null; + if (account.id) updatedAccount = await ctx.context.internalAdapter.updateAccount(account.id, updatedData); + if (ctx.context.options.account?.storeAccountCookie) await setAccountCookie(ctx, { + ...account, + ...updatedAccount ?? updatedData + }); + } + const accessTokenExpiresAt = (() => { + if (newTokens?.accessTokenExpiresAt) { + if (typeof newTokens.accessTokenExpiresAt === "string") return new Date(newTokens.accessTokenExpiresAt); + return newTokens.accessTokenExpiresAt; + } + if (account.accessTokenExpiresAt) { + if (typeof account.accessTokenExpiresAt === "string") return new Date(account.accessTokenExpiresAt); + return account.accessTokenExpiresAt; + } + })(); + const tokens = { + accessToken: newTokens?.accessToken ?? await decryptOAuthToken(account.accessToken ?? "", ctx.context), + accessTokenExpiresAt, + scopes: account.scope?.split(",") ?? [], + idToken: newTokens?.idToken ?? account.idToken ?? void 0 + }; + return ctx.json(tokens); + } catch (error2) { + throw new APIError("BAD_REQUEST", { + message: "Failed to get a valid access token", + cause: error2 + }); + } +}); +const refreshToken = createAuthEndpoint("/refresh-token", { + method: "POST", + body: z.object({ + providerId: z.string().meta({ description: "The provider ID for the OAuth provider" }), + accountId: z.string().meta({ description: "The account ID associated with the refresh token" }).optional(), + userId: z.string().meta({ description: "The user ID associated with the account" }).optional() + }), + metadata: { openapi: { + description: "Refresh the access token using a refresh token", + responses: { + 200: { + description: "Access token refreshed successfully", + content: { "application/json": { schema: { + type: "object", + properties: { + tokenType: { type: "string" }, + idToken: { type: "string" }, + accessToken: { type: "string" }, + refreshToken: { type: "string" }, + accessTokenExpiresAt: { + type: "string", + format: "date-time" + }, + refreshTokenExpiresAt: { + type: "string", + format: "date-time" + } + } + } } } + }, + 400: { description: "Invalid refresh token or provider configuration" } + } + } } +}, async (ctx) => { + const { providerId, accountId, userId } = ctx.body; + const req = ctx.request; + const session = await getSessionFromCtx(ctx); + if (req && !session) throw ctx.error("UNAUTHORIZED"); + const resolvedUserId = session?.user?.id || userId; + if (!resolvedUserId) throw new APIError("BAD_REQUEST", { message: `Either userId or session is required` }); + const provider = ctx.context.socialProviders.find((p) => p.id === providerId); + if (!provider) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} not found.` }); + if (!provider.refreshAccessToken) throw new APIError("BAD_REQUEST", { message: `Provider ${providerId} does not support token refreshing.` }); + let account = void 0; + const accountData = await getAccountCookie(ctx); + if (accountData && (!providerId || providerId === accountData?.providerId)) account = accountData; + else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.id === accountId && acc.providerId === providerId : acc.providerId === providerId); + if (!account) throw new APIError("BAD_REQUEST", { message: "Account not found" }); + let refreshToken$1 = void 0; + if (accountData && providerId === accountData.providerId) refreshToken$1 = accountData.refreshToken ?? void 0; + else refreshToken$1 = account.refreshToken ?? void 0; + if (!refreshToken$1) throw new APIError("BAD_REQUEST", { message: "Refresh token not found" }); + try { + const decryptedRefreshToken = await decryptOAuthToken(refreshToken$1, ctx.context); + const tokens = await provider.refreshAccessToken(decryptedRefreshToken); + if (account.id) { + const updateData = { + ...account || {}, + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") || account.scope, + idToken: tokens.idToken || account.idToken + }; + await ctx.context.internalAdapter.updateAccount(account.id, updateData); + } + if (accountData && providerId === accountData.providerId && ctx.context.options.account?.storeAccountCookie) await setAccountCookie(ctx, { + ...accountData, + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") || accountData.scope, + idToken: tokens.idToken || accountData.idToken + }); + return ctx.json({ + accessToken: tokens.accessToken, + refreshToken: tokens.refreshToken, + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") || account.scope, + idToken: tokens.idToken || account.idToken, + providerId: account.providerId, + accountId: account.accountId + }); + } catch (error2) { + throw new APIError("BAD_REQUEST", { + message: "Failed to refresh access token", + cause: error2 + }); + } +}); +const accountInfoQuerySchema = z.optional(z.object({ accountId: z.string().meta({ description: "The provider given account id for which to get the account info" }).optional() })); +const accountInfo = createAuthEndpoint("/account-info", { + method: "GET", + use: [sessionMiddleware], + metadata: { openapi: { + description: "Get the account info provided by the provider", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + user: { + type: "object", + properties: { + id: { type: "string" }, + name: { type: "string" }, + email: { type: "string" }, + image: { type: "string" }, + emailVerified: { type: "boolean" } + }, + required: ["id", "emailVerified"] + }, + data: { + type: "object", + properties: {}, + additionalProperties: true + } + }, + required: ["user", "data"], + additionalProperties: false + } } } + } } + } }, + query: accountInfoQuerySchema +}, async (ctx) => { + const providedAccountId = ctx.query?.accountId; + let account = void 0; + if (!providedAccountId) { + if (ctx.context.options.account?.storeAccountCookie) { + const accountData = await getAccountCookie(ctx); + if (accountData) account = accountData; + } + } else { + const accountData = await ctx.context.internalAdapter.findAccount(providedAccountId); + if (accountData) account = accountData; + } + if (!account || account.userId !== ctx.context.session.user.id) throw new APIError("BAD_REQUEST", { message: "Account not found" }); + const provider = ctx.context.socialProviders.find((p) => p.id === account.providerId); + if (!provider) throw new APIError("INTERNAL_SERVER_ERROR", { message: `Provider account provider is ${account.providerId} but it is not configured` }); + const tokens = await getAccessToken({ + ...ctx, + method: "POST", + body: { + accountId: account.id, + providerId: account.providerId + }, + returnHeaders: false, + returnStatus: false + }); + if (!tokens.accessToken) throw new APIError("BAD_REQUEST", { message: "Access token not found" }); + const info2 = await provider.getUserInfo({ + ...tokens, + accessToken: tokens.accessToken + }); + return ctx.json(info2); +}); +async function createEmailVerificationToken(secret, email, updateTo, expiresIn = 3600, extraPayload) { + return await signJWT({ + email: email.toLowerCase(), + updateTo, + ...extraPayload + }, secret, expiresIn); +} +async function sendVerificationEmailFn(ctx, user) { + if (!ctx.context.options.emailVerification?.sendVerificationEmail) { + ctx.context.logger.error("Verification email isn't enabled."); + throw new APIError("BAD_REQUEST", { message: "Verification email isn't enabled" }); + } + const token = await createEmailVerificationToken(ctx.context.secret, user.email, void 0, ctx.context.options.emailVerification?.expiresIn); + const callbackURL = ctx.body.callbackURL ? encodeURIComponent(ctx.body.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user, + url, + token + }, ctx.request)); +} +const sendVerificationEmail = createAuthEndpoint("/send-verification-email", { + method: "POST", + operationId: "sendVerificationEmail", + body: z.object({ + email: z.email().meta({ description: "The email to send the verification email to" }), + callbackURL: z.string().meta({ description: "The URL to use for email verification callback" }).optional() + }), + metadata: { openapi: { + operationId: "sendVerificationEmail", + description: "Send a verification email to the user", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + email: { + type: "string", + description: "The email to send the verification email to", + example: "user@example.com" + }, + callbackURL: { + type: "string", + description: "The URL to use for email verification callback", + example: "https://example.com/callback", + nullable: true + } + }, + required: ["email"] + } } } }, + responses: { + "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { + type: "boolean", + description: "Indicates if the email was sent successfully", + example: true + } } + } } } + }, + "400": { + description: "Bad Request", + content: { "application/json": { schema: { + type: "object", + properties: { message: { + type: "string", + description: "Error message", + example: "Verification email isn't enabled" + } } + } } } + } + } + } } +}, async (ctx) => { + if (!ctx.context.options.emailVerification?.sendVerificationEmail) { + ctx.context.logger.error("Verification email isn't enabled."); + throw new APIError("BAD_REQUEST", { message: "Verification email isn't enabled" }); + } + const { email } = ctx.body; + const session = await getSessionFromCtx(ctx); + if (!session) { + const user = await ctx.context.internalAdapter.findUserByEmail(email); + if (!user || user.user.emailVerified) { + await createEmailVerificationToken(ctx.context.secret, email, void 0, ctx.context.options.emailVerification?.expiresIn); + return ctx.json({ status: true }); + } + await sendVerificationEmailFn(ctx, user.user); + return ctx.json({ status: true }); + } + if (session?.user.email !== email) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.EMAIL_MISMATCH }); + if (session?.user.emailVerified) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.EMAIL_ALREADY_VERIFIED }); + await sendVerificationEmailFn(ctx, session.user); + return ctx.json({ status: true }); +}); +const verifyEmail = createAuthEndpoint("/verify-email", { + method: "GET", + operationId: "verifyEmail", + query: z.object({ + token: z.string().meta({ description: "The token to verify the email" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional() + }), + use: [originCheck((ctx) => ctx.query.callbackURL)], + metadata: { openapi: { + description: "Verify the email of the user", + parameters: [{ + name: "token", + in: "query", + description: "The token to verify the email", + required: true, + schema: { type: "string" } + }, { + name: "callbackURL", + in: "query", + description: "The URL to redirect to after email verification", + required: false, + schema: { type: "string" } + }], + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + user: { + type: "object", + $ref: "#/components/schemas/User" + }, + status: { + type: "boolean", + description: "Indicates if the email was verified successfully" + } + }, + required: ["user", "status"] + } } } + } } + } } +}, async (ctx) => { + function redirectOnError(error2) { + if (ctx.query.callbackURL) { + if (ctx.query.callbackURL.includes("?")) throw ctx.redirect(`${ctx.query.callbackURL}&error=${error2}`); + throw ctx.redirect(`${ctx.query.callbackURL}?error=${error2}`); + } + throw new APIError("UNAUTHORIZED", { message: error2 }); + } + const { token } = ctx.query; + let jwt; + try { + jwt = await jwtVerify(token, new TextEncoder().encode(ctx.context.secret), { algorithms: ["HS256"] }); + } catch (e) { + if (e instanceof JWTExpired) return redirectOnError("token_expired"); + return redirectOnError("invalid_token"); + } + const parsed = z.object({ + email: z.email(), + updateTo: z.string().optional(), + requestType: z.string().optional() + }).parse(jwt.payload); + const user = await ctx.context.internalAdapter.findUserByEmail(parsed.email); + if (!user) return redirectOnError("user_not_found"); + if (parsed.updateTo) { + const session = await getSessionFromCtx(ctx); + if (session && session.user.email !== parsed.email) return redirectOnError("unauthorized"); + switch (parsed.requestType) { + case "change-email-confirmation": { + const newToken = await createEmailVerificationToken(ctx.context.secret, parsed.email, parsed.updateTo, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-verification" }); + const updateCallbackURL = ctx.query.callbackURL ? encodeURIComponent(ctx.query.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${newToken}&callbackURL=${updateCallbackURL}`; + if (ctx.context.options.emailVerification?.sendVerificationEmail) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: { + ...user.user, + email: parsed.updateTo + }, + url, + token: newToken + }, ctx.request)); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ status: true }); + } + case "change-email-verification": { + let activeSession = session; + if (!activeSession) { + const newSession = await ctx.context.internalAdapter.createSession(user.user.id); + if (!newSession) throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + activeSession = { + session: newSession, + user: user.user + }; + } + if (ctx.context.options.emailVerification?.onEmailVerification) await ctx.context.options.emailVerification.onEmailVerification(user.user, ctx.request); + const updatedUser$1 = await ctx.context.internalAdapter.updateUserByEmail(parsed.email, { + email: parsed.updateTo, + emailVerified: true + }); + if (ctx.context.options.emailVerification?.afterEmailVerification) await ctx.context.options.emailVerification.afterEmailVerification(updatedUser$1, ctx.request); + await setSessionCookie(ctx, { + session: activeSession.session, + user: { + ...activeSession.user, + email: parsed.updateTo, + emailVerified: true + } + }); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: parseUserOutput(ctx.context.options, updatedUser$1) + }); + } + default: { + let activeSession = session; + if (!activeSession) { + const newSession = await ctx.context.internalAdapter.createSession(user.user.id); + if (!newSession) throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + activeSession = { + session: newSession, + user: user.user + }; + } + const updatedUser$1 = await ctx.context.internalAdapter.updateUserByEmail(parsed.email, { + email: parsed.updateTo, + emailVerified: false + }); + const newToken = await createEmailVerificationToken(ctx.context.secret, parsed.updateTo); + const updateCallbackURL = ctx.query.callbackURL ? encodeURIComponent(ctx.query.callbackURL) : encodeURIComponent("/"); + if (ctx.context.options.emailVerification?.sendVerificationEmail) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: updatedUser$1, + url: `${ctx.context.baseURL}/verify-email?token=${newToken}&callbackURL=${updateCallbackURL}`, + token: newToken + }, ctx.request)); + await setSessionCookie(ctx, { + session: activeSession.session, + user: { + ...activeSession.user, + email: parsed.updateTo, + emailVerified: false + } + }); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: parseUserOutput(ctx.context.options, updatedUser$1) + }); + } + } + } + if (user.user.emailVerified) { + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: null + }); + } + if (ctx.context.options.emailVerification?.beforeEmailVerification) await ctx.context.options.emailVerification.beforeEmailVerification(user.user, ctx.request); + if (ctx.context.options.emailVerification?.onEmailVerification) await ctx.context.options.emailVerification.onEmailVerification(user.user, ctx.request); + const updatedUser = await ctx.context.internalAdapter.updateUserByEmail(parsed.email, { emailVerified: true }); + if (ctx.context.options.emailVerification?.afterEmailVerification) await ctx.context.options.emailVerification.afterEmailVerification(updatedUser, ctx.request); + if (ctx.context.options.emailVerification?.autoSignInAfterVerification) { + const currentSession = await getSessionFromCtx(ctx); + if (!currentSession || currentSession.user.email !== parsed.email) { + const session = await ctx.context.internalAdapter.createSession(user.user.id); + if (!session) throw new APIError("INTERNAL_SERVER_ERROR", { message: "Failed to create session" }); + await setSessionCookie(ctx, { + session, + user: { + ...user.user, + emailVerified: true + } + }); + } else await setSessionCookie(ctx, { + session: currentSession.session, + user: { + ...currentSession.user, + emailVerified: true + } + }); + } + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL); + return ctx.json({ + status: true, + user: null + }); +}); +async function handleOAuthUserInfo(c, opts) { + const { userInfo, account, callbackURL, disableSignUp, overrideUserInfo } = opts; + const dbUser = await c.context.internalAdapter.findOAuthUser(userInfo.email.toLowerCase(), account.accountId, account.providerId).catch((e) => { + logger.error("Better auth was unable to query your database.\nError: ", e); + const errorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`; + throw c.redirect(`${errorURL}?error=internal_server_error`); + }); + let user = dbUser?.user; + const isRegister = !user; + if (dbUser) { + const linkedAccount = dbUser.linkedAccount ?? dbUser.accounts.find((acc) => acc.providerId === account.providerId && acc.accountId === account.accountId); + if (!linkedAccount) { + const accountLinking = c.context.options.account?.accountLinking; + const trustedProviders = c.context.options.account?.accountLinking?.trustedProviders; + if (!(opts.isTrustedProvider || trustedProviders?.includes(account.providerId)) && !userInfo.emailVerified || accountLinking?.enabled === false || accountLinking?.disableImplicitLinking === true) { + if (isDevelopment()) logger.warn(`User already exist but account isn't linked to ${account.providerId}. To read more about how account linking works in Better Auth see https://www.better-auth.com/docs/concepts/users-accounts#account-linking.`); + return { + error: "account not linked", + data: null + }; + } + try { + await c.context.internalAdapter.linkAccount({ + providerId: account.providerId, + accountId: userInfo.id.toString(), + userId: dbUser.user.id, + accessToken: await setTokenUtil(account.accessToken, c.context), + refreshToken: await setTokenUtil(account.refreshToken, c.context), + idToken: account.idToken, + accessTokenExpiresAt: account.accessTokenExpiresAt, + refreshTokenExpiresAt: account.refreshTokenExpiresAt, + scope: account.scope + }); + } catch (e) { + logger.error("Unable to link account", e); + return { + error: "unable to link account", + data: null + }; + } + if (userInfo.emailVerified && !dbUser.user.emailVerified && userInfo.email.toLowerCase() === dbUser.user.email) await c.context.internalAdapter.updateUser(dbUser.user.id, { emailVerified: true }); + } else { + const freshTokens = c.context.options.account?.updateAccountOnSignIn !== false ? Object.fromEntries(Object.entries({ + idToken: account.idToken, + accessToken: await setTokenUtil(account.accessToken, c.context), + refreshToken: await setTokenUtil(account.refreshToken, c.context), + accessTokenExpiresAt: account.accessTokenExpiresAt, + refreshTokenExpiresAt: account.refreshTokenExpiresAt, + scope: account.scope + }).filter(([_, value]) => value !== void 0)) : {}; + if (c.context.options.account?.storeAccountCookie) await setAccountCookie(c, { + ...linkedAccount, + ...freshTokens + }); + if (Object.keys(freshTokens).length > 0) await c.context.internalAdapter.updateAccount(linkedAccount.id, freshTokens); + if (userInfo.emailVerified && !dbUser.user.emailVerified && userInfo.email.toLowerCase() === dbUser.user.email) await c.context.internalAdapter.updateUser(dbUser.user.id, { emailVerified: true }); + } + if (overrideUserInfo) { + const { id: _, ...restUserInfo } = userInfo; + user = await c.context.internalAdapter.updateUser(dbUser.user.id, { + ...restUserInfo, + email: userInfo.email.toLowerCase(), + emailVerified: userInfo.email.toLowerCase() === dbUser.user.email ? dbUser.user.emailVerified || userInfo.emailVerified : userInfo.emailVerified + }); + } + } else { + if (disableSignUp) return { + error: "signup disabled", + data: null, + isRegister: false + }; + try { + const { id: _, ...restUserInfo } = userInfo; + const accountData = { + accessToken: await setTokenUtil(account.accessToken, c.context), + refreshToken: await setTokenUtil(account.refreshToken, c.context), + idToken: account.idToken, + accessTokenExpiresAt: account.accessTokenExpiresAt, + refreshTokenExpiresAt: account.refreshTokenExpiresAt, + scope: account.scope, + providerId: account.providerId, + accountId: userInfo.id.toString() + }; + const { user: createdUser, account: createdAccount } = await c.context.internalAdapter.createOAuthUser({ + ...restUserInfo, + email: userInfo.email.toLowerCase() + }, accountData); + user = createdUser; + if (c.context.options.account?.storeAccountCookie) await setAccountCookie(c, createdAccount); + if (!userInfo.emailVerified && user && c.context.options.emailVerification?.sendOnSignUp && c.context.options.emailVerification?.sendVerificationEmail) { + const token = await createEmailVerificationToken(c.context.secret, user.email, void 0, c.context.options.emailVerification?.expiresIn); + const url = `${c.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + await c.context.runInBackgroundOrAwait(c.context.options.emailVerification.sendVerificationEmail({ + user, + url, + token + }, c.request)); + } + } catch (e) { + logger.error(e); + if (e instanceof APIError) return { + error: e.message, + data: null, + isRegister: false + }; + return { + error: "unable to create user", + data: null, + isRegister: false + }; + } + } + if (!user) return { + error: "unable to create user", + data: null, + isRegister: false + }; + const session = await c.context.internalAdapter.createSession(user.id); + if (!session) return { + error: "unable to create session", + data: null, + isRegister: false + }; + return { + data: { + session, + user + }, + error: null, + isRegister + }; +} +const HIDE_METADATA = { scope: "server" }; +const schema = z.object({ + code: z.string().optional(), + error: z.string().optional(), + device_id: z.string().optional(), + error_description: z.string().optional(), + state: z.string().optional(), + user: z.string().optional() +}); +const callbackOAuth = createAuthEndpoint("/callback/:id", { + method: ["GET", "POST"], + operationId: "handleOAuthCallback", + body: schema.optional(), + query: schema.optional(), + metadata: { + ...HIDE_METADATA, + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"] + } +}, async (c) => { + let queryOrBody; + const defaultErrorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`; + if (c.method === "POST") { + const postData = c.body ? schema.parse(c.body) : {}; + const queryData = c.query ? schema.parse(c.query) : {}; + const mergedData = schema.parse({ + ...postData, + ...queryData + }); + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(mergedData)) if (value !== void 0 && value !== null) params.set(key, String(value)); + const redirectURL = `${c.context.baseURL}/callback/${c.params.id}?${params.toString()}`; + throw c.redirect(redirectURL); + } + try { + if (c.method === "GET") queryOrBody = schema.parse(c.query); + else if (c.method === "POST") queryOrBody = schema.parse(c.body); + else throw new Error("Unsupported method"); + } catch (e) { + c.context.logger.error("INVALID_CALLBACK_REQUEST", e); + throw c.redirect(`${defaultErrorURL}?error=invalid_callback_request`); + } + const { code, error: error2, state, error_description, device_id, user: userData } = queryOrBody; + if (!state) { + c.context.logger.error("State not found", error2); + const url = `${defaultErrorURL}${defaultErrorURL.includes("?") ? "&" : "?"}state=state_not_found`; + throw c.redirect(url); + } + const { codeVerifier, callbackURL, link, errorURL, newUserURL, requestSignUp } = await parseState(c); + function redirectOnError(error$1, description) { + const baseURL = errorURL ?? defaultErrorURL; + const params = new URLSearchParams({ error: error$1 }); + if (description) params.set("error_description", description); + const url = `${baseURL}${baseURL.includes("?") ? "&" : "?"}${params.toString()}`; + throw c.redirect(url); + } + if (error2) redirectOnError(error2, error_description); + if (!code) { + c.context.logger.error("Code not found"); + throw redirectOnError("no_code"); + } + const provider = c.context.socialProviders.find((p) => p.id === c.params.id); + if (!provider) { + c.context.logger.error("Oauth provider with id", c.params.id, "not found"); + throw redirectOnError("oauth_provider_not_found"); + } + let tokens; + try { + tokens = await provider.validateAuthorizationCode({ + code, + codeVerifier, + deviceId: device_id, + redirectURI: `${c.context.baseURL}/callback/${provider.id}` + }); + } catch (e) { + c.context.logger.error("", e); + throw redirectOnError("invalid_code"); + } + if (!tokens) throw redirectOnError("invalid_code"); + const parsedUserData = userData ? safeJSONParse(userData) : null; + const userInfo = await provider.getUserInfo({ + ...tokens, + user: parsedUserData ?? void 0 + }).then((res) => res?.user); + if (!userInfo) { + c.context.logger.error("Unable to get user info"); + return redirectOnError("unable_to_get_user_info"); + } + if (!callbackURL) { + c.context.logger.error("No callback URL found"); + throw redirectOnError("no_callback_url"); + } + if (link) { + if (!c.context.options.account?.accountLinking?.trustedProviders?.includes(provider.id) && !userInfo.emailVerified || c.context.options.account?.accountLinking?.enabled === false) { + c.context.logger.error("Unable to link account - untrusted provider"); + return redirectOnError("unable_to_link_account"); + } + if (userInfo.email?.toLowerCase() !== link.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) return redirectOnError("email_doesn't_match"); + const existingAccount = await c.context.internalAdapter.findAccount(String(userInfo.id)); + if (existingAccount) { + if (existingAccount.userId.toString() !== link.userId.toString()) return redirectOnError("account_already_linked_to_different_user"); + const updateData = Object.fromEntries(Object.entries({ + accessToken: await setTokenUtil(tokens.accessToken, c.context), + refreshToken: await setTokenUtil(tokens.refreshToken, c.context), + idToken: tokens.idToken, + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") + }).filter(([_, value]) => value !== void 0)); + await c.context.internalAdapter.updateAccount(existingAccount.id, updateData); + } else if (!await c.context.internalAdapter.createAccount({ + userId: link.userId, + providerId: provider.id, + accountId: String(userInfo.id), + ...tokens, + accessToken: await setTokenUtil(tokens.accessToken, c.context), + refreshToken: await setTokenUtil(tokens.refreshToken, c.context), + scope: tokens.scopes?.join(",") + })) return redirectOnError("unable_to_link_account"); + let toRedirectTo$1; + try { + toRedirectTo$1 = callbackURL.toString(); + } catch { + toRedirectTo$1 = callbackURL; + } + throw c.redirect(toRedirectTo$1); + } + if (!userInfo.email) { + c.context.logger.error("Provider did not return email. This could be due to misconfiguration in the provider settings."); + return redirectOnError("email_not_found"); + } + const accountData = { + providerId: provider.id, + accountId: String(userInfo.id), + ...tokens, + scope: tokens.scopes?.join(",") + }; + const result = await handleOAuthUserInfo(c, { + userInfo: { + ...userInfo, + id: String(userInfo.id), + email: userInfo.email, + name: userInfo.name || userInfo.email + }, + account: accountData, + callbackURL, + disableSignUp: provider.disableImplicitSignUp && !requestSignUp || provider.options?.disableSignUp, + overrideUserInfo: provider.options?.overrideUserInfoOnSignIn + }); + if (result.error) { + c.context.logger.error(result.error.split(" ").join("_")); + return redirectOnError(result.error.split(" ").join("_")); + } + const { session, user } = result.data; + await setSessionCookie(c, { + session, + user + }); + let toRedirectTo; + try { + toRedirectTo = (result.isRegister ? newUserURL || callbackURL : callbackURL).toString(); + } catch { + toRedirectTo = result.isRegister ? newUserURL || callbackURL : callbackURL; + } + throw c.redirect(toRedirectTo); +}); +function sanitize(input) { + return input.replace(//g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/&(?!amp;|lt;|gt;|quot;|#39;|#x[0-9a-fA-F]+;|#[0-9]+;)/g, "&"); +} +const html = (options, code = "Unknown", description = null) => { + const custom = options.onAPIError?.customizeDefaultErrorPage; + return ` + + + + + Error + + + +
+${custom?.disableBackgroundGrid ? "" : ` +
+
+`} + +
+ ${custom?.disableCornerDecorations ? "" : ` + +
+
+ +
+
`} + +
+
+
+

+ ERROR +

+
+
+
+ +

+ Something went wrong +

+ +
+ + CODE: + + + ${sanitize(code)} + +
+ +

+ ${!description ? `We encountered an unexpected error. Please try again or return to the home page. If you're a developer, you can find more information about the error here.` : description} +

+
+ + +
+
+ +`; +}; +const error = createAuthEndpoint("/error", { + method: "GET", + metadata: { + ...HIDE_METADATA, + openapi: { + description: "Displays an error page", + responses: { "200": { + description: "Success", + content: { "text/html": { schema: { + type: "string", + description: "The HTML content of the error page" + } } } + } } + } + } +}, async (c) => { + const url = new URL(c.request?.url || ""); + const unsanitizedCode = url.searchParams.get("error") || "UNKNOWN"; + const unsanitizedDescription = url.searchParams.get("error_description") || null; + const safeCode = /^[\'A-Za-z0-9_-]+$/.test(unsanitizedCode) ? unsanitizedCode : "UNKNOWN"; + const safeDescription = unsanitizedDescription ? sanitize(unsanitizedDescription) : null; + const queryParams = new URLSearchParams(); + queryParams.set("error", safeCode); + if (unsanitizedDescription) queryParams.set("error_description", unsanitizedDescription); + const options = c.context.options; + const errorURL = options.onAPIError?.errorURL; + if (errorURL) return new Response(null, { + status: 302, + headers: { Location: `${errorURL}${errorURL.includes("?") ? "&" : "?"}${queryParams.toString()}` } + }); + if (isProduction && !options.onAPIError?.customizeDefaultErrorPage) return new Response(null, { + status: 302, + headers: { Location: `/?${queryParams.toString()}` } + }); + return new Response(html(c.context.options, safeCode, safeDescription), { headers: { "Content-Type": "text/html" } }); +}); +const ok = createAuthEndpoint("/ok", { + method: "GET", + metadata: { + ...HIDE_METADATA, + openapi: { + description: "Check if the API is working", + responses: { "200": { + description: "API is working", + content: { "application/json": { schema: { + type: "object", + properties: { ok: { + type: "boolean", + description: "Indicates if the API is working" + } }, + required: ["ok"] + } } } + } } + } + } +}, async (ctx) => { + return ctx.json({ ok: true }); +}); +async function validatePassword(ctx, data) { + const credentialAccount = (await ctx.context.internalAdapter.findAccounts(data.userId))?.find((account) => account.providerId === "credential"); + const currentPassword = credentialAccount?.password; + if (!credentialAccount || !currentPassword) return false; + return await ctx.context.password.verify({ + hash: currentPassword, + password: data.password + }); +} +async function checkPassword(userId, c) { + const credentialAccount = (await c.context.internalAdapter.findAccounts(userId))?.find((account) => account.providerId === "credential"); + const currentPassword = credentialAccount?.password; + if (!credentialAccount || !currentPassword || !c.body.password) throw new APIError("BAD_REQUEST", { message: "No password credential found" }); + if (!await c.context.password.verify({ + hash: currentPassword, + password: c.body.password + })) throw new APIError("BAD_REQUEST", { message: "Invalid password" }); + return true; +} +function redirectError(ctx, callbackURL, query) { + const url = callbackURL ? new URL(callbackURL, ctx.baseURL) : new URL(`${ctx.baseURL}/error`); + if (query) Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v)); + return url.href; +} +function redirectCallback(ctx, callbackURL, query) { + const url = new URL(callbackURL, ctx.baseURL); + if (query) Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v)); + return url.href; +} +const requestPasswordReset = createAuthEndpoint("/request-password-reset", { + method: "POST", + body: z.object({ + email: z.email().meta({ description: "The email address of the user to send a password reset email to" }), + redirectTo: z.string().meta({ description: "The URL to redirect the user to reset their password. If the token isn't valid or expired, it'll be redirected with a query parameter `?error=INVALID_TOKEN`. If the token is valid, it'll be redirected with a query parameter `?token=VALID_TOKEN" }).optional() + }), + metadata: { openapi: { + operationId: "requestPasswordReset", + description: "Send a password reset email to the user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { + status: { type: "boolean" }, + message: { type: "string" } + } + } } } + } } + } } +}, async (ctx) => { + if (!ctx.context.options.emailAndPassword?.sendResetPassword) { + ctx.context.logger.error("Reset password isn't enabled.Please pass an emailAndPassword.sendResetPassword function in your auth config!"); + throw new APIError("BAD_REQUEST", { message: "Reset password isn't enabled" }); + } + const { email, redirectTo } = ctx.body; + const user = await ctx.context.internalAdapter.findUserByEmail(email, { includeAccounts: true }); + if (!user) { + generateId(24); + await ctx.context.internalAdapter.findVerificationValue("dummy-verification-token"); + ctx.context.logger.error("Reset Password: User not found", { email }); + return ctx.json({ + status: true, + message: "If this email exists in our system, check your email for the reset link" + }); + } + const expiresAt = getDate(ctx.context.options.emailAndPassword.resetPasswordTokenExpiresIn || 3600 * 1, "sec"); + const verificationToken = generateId(24); + await ctx.context.internalAdapter.createVerificationValue({ + value: user.user.id, + identifier: `reset-password:${verificationToken}`, + expiresAt + }); + const callbackURL = redirectTo ? encodeURIComponent(redirectTo) : ""; + const url = `${ctx.context.baseURL}/reset-password/${verificationToken}?callbackURL=${callbackURL}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailAndPassword.sendResetPassword({ + user: user.user, + url, + token: verificationToken + }, ctx.request)); + return ctx.json({ + status: true, + message: "If this email exists in our system, check your email for the reset link" + }); +}); +const requestPasswordResetCallback = createAuthEndpoint("/reset-password/:token", { + method: "GET", + operationId: "forgetPasswordCallback", + query: z.object({ callbackURL: z.string().meta({ description: "The URL to redirect the user to reset their password" }) }), + use: [originCheck((ctx) => ctx.query.callbackURL)], + metadata: { openapi: { + operationId: "resetPasswordCallback", + description: "Redirects the user to the callback URL with the token", + parameters: [{ + name: "token", + in: "path", + required: true, + description: "The token to reset the password", + schema: { type: "string" } + }, { + name: "callbackURL", + in: "query", + required: true, + description: "The URL to redirect the user to reset their password", + schema: { type: "string" } + }], + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { token: { type: "string" } } + } } } + } } + } } +}, async (ctx) => { + const { token } = ctx.params; + const { callbackURL } = ctx.query; + if (!token || !callbackURL) throw ctx.redirect(redirectError(ctx.context, callbackURL, { error: "INVALID_TOKEN" })); + const verification = await ctx.context.internalAdapter.findVerificationValue(`reset-password:${token}`); + if (!verification || verification.expiresAt < /* @__PURE__ */ new Date()) throw ctx.redirect(redirectError(ctx.context, callbackURL, { error: "INVALID_TOKEN" })); + throw ctx.redirect(redirectCallback(ctx.context, callbackURL, { token })); +}); +const resetPassword = createAuthEndpoint("/reset-password", { + method: "POST", + operationId: "resetPassword", + query: z.object({ token: z.string().optional() }).optional(), + body: z.object({ + newPassword: z.string().meta({ description: "The new password to set" }), + token: z.string().meta({ description: "The token to reset the password" }).optional() + }), + metadata: { openapi: { + operationId: "resetPassword", + description: "Reset the password for a user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const token = ctx.body.token || ctx.query?.token; + if (!token) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + const { newPassword } = ctx.body; + const minLength = ctx.context.password?.config.minPasswordLength; + const maxLength = ctx.context.password?.config.maxPasswordLength; + if (newPassword.length < minLength) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + if (newPassword.length > maxLength) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + const id = `reset-password:${token}`; + const verification = await ctx.context.internalAdapter.findVerificationValue(id); + if (!verification || verification.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + const userId = verification.value; + const hashedPassword = await ctx.context.password.hash(newPassword); + if (!(await ctx.context.internalAdapter.findAccounts(userId)).find((ac) => ac.providerId === "credential")) await ctx.context.internalAdapter.createAccount({ + userId, + providerId: "credential", + password: hashedPassword, + accountId: userId + }); + else await ctx.context.internalAdapter.updatePassword(userId, hashedPassword); + await ctx.context.internalAdapter.deleteVerificationValue(verification.id); + if (ctx.context.options.emailAndPassword?.onPasswordReset) { + const user = await ctx.context.internalAdapter.findUserById(userId); + if (user) await ctx.context.options.emailAndPassword.onPasswordReset({ user }, ctx.request); + } + if (ctx.context.options.emailAndPassword?.revokeSessionsOnPasswordReset) await ctx.context.internalAdapter.deleteSessions(userId); + return ctx.json({ status: true }); +}); +const verifyPassword = createAuthEndpoint("/verify-password", { + method: "POST", + body: z.object({ password: z.string().meta({ description: "The password to verify" }) }), + metadata: { + scope: "server", + openapi: { + operationId: "verifyPassword", + description: "Verify the current user's password", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { status: { type: "boolean" } } + } } } + } } + } + }, + use: [sensitiveSessionMiddleware] +}, async (ctx) => { + const { password } = ctx.body; + const session = ctx.context.session; + if (!await validatePassword(ctx, { + password, + userId: session.user.id + })) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + return ctx.json({ status: true }); +}); +const socialSignInBodySchema = z.object({ + callbackURL: z.string().meta({ description: "Callback URL to redirect to after the user has signed in" }).optional(), + newUserCallbackURL: z.string().optional(), + errorCallbackURL: z.string().meta({ description: "Callback URL to redirect to if an error happens" }).optional(), + provider: SocialProviderListEnum, + disableRedirect: z.boolean().meta({ description: "Disable automatic redirection to the provider. Useful for handling the redirection yourself" }).optional(), + idToken: z.optional(z.object({ + token: z.string().meta({ description: "ID token from the provider" }), + nonce: z.string().meta({ description: "Nonce used to generate the token" }).optional(), + accessToken: z.string().meta({ description: "Access token from the provider" }).optional(), + refreshToken: z.string().meta({ description: "Refresh token from the provider" }).optional(), + expiresAt: z.number().meta({ description: "Expiry date of the token" }).optional() + })), + scopes: z.array(z.string()).meta({ description: "Array of scopes to request from the provider. This will override the default scopes passed." }).optional(), + requestSignUp: z.boolean().meta({ description: "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider" }).optional(), + loginHint: z.string().meta({ description: "The login hint to use for the authorization code request" }).optional(), + additionalData: z.record(z.string(), z.any()).optional().meta({ description: "Additional data to be passed through the OAuth flow" }) +}); +const signInSocial = () => createAuthEndpoint("/sign-in/social", { + method: "POST", + operationId: "socialSignIn", + body: socialSignInBodySchema, + metadata: { + $Infer: { + body: {}, + returned: {} + }, + openapi: { + description: "Sign in with a social provider", + operationId: "socialSignIn", + responses: { "200": { + description: "Success - Returns either session details or redirect URL", + content: { "application/json": { schema: { + type: "object", + description: "Session response when idToken is provided", + properties: { + token: { type: "string" }, + user: { + type: "object", + $ref: "#/components/schemas/User" + }, + url: { type: "string" }, + redirect: { + type: "boolean", + enum: [false] + } + }, + required: [ + "redirect", + "token", + "user" + ] + } } } + } } + } + } +}, async (c) => { + const provider = c.context.socialProviders.find((p) => p.id === c.body.provider); + if (!provider) { + c.context.logger.error("Provider not found. Make sure to add the provider in your auth config", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.PROVIDER_NOT_FOUND }); + } + if (c.body.idToken) { + if (!provider.verifyIdToken) { + c.context.logger.error("Provider does not support id token verification", { provider: c.body.provider }); + throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.ID_TOKEN_NOT_SUPPORTED }); + } + const { token, nonce } = c.body.idToken; + if (!await provider.verifyIdToken(token, nonce)) { + c.context.logger.error("Invalid id token", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + } + const userInfo = await provider.getUserInfo({ + idToken: token, + accessToken: c.body.idToken.accessToken, + refreshToken: c.body.idToken.refreshToken + }); + if (!userInfo || !userInfo?.user) { + c.context.logger.error("Failed to get user info", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO }); + } + if (!userInfo.user.email) { + c.context.logger.error("User email not found", { provider: c.body.provider }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.USER_EMAIL_NOT_FOUND }); + } + const data = await handleOAuthUserInfo(c, { + userInfo: { + ...userInfo.user, + email: userInfo.user.email, + id: String(userInfo.user.id), + name: userInfo.user.name || "", + image: userInfo.user.image, + emailVerified: userInfo.user.emailVerified || false + }, + account: { + providerId: provider.id, + accountId: String(userInfo.user.id), + accessToken: c.body.idToken.accessToken + }, + callbackURL: c.body.callbackURL, + disableSignUp: provider.disableImplicitSignUp && !c.body.requestSignUp || provider.disableSignUp + }); + if (data.error) throw new APIError("UNAUTHORIZED", { message: data.error }); + await setSessionCookie(c, data.data); + return c.json({ + redirect: false, + token: data.data.session.token, + url: void 0, + user: parseUserOutput(c.context.options, data.data.user) + }); + } + const { codeVerifier, state } = await generateState(c, void 0, c.body.additionalData); + const url = await provider.createAuthorizationURL({ + state, + codeVerifier, + redirectURI: `${c.context.baseURL}/callback/${provider.id}`, + scopes: c.body.scopes, + loginHint: c.body.loginHint + }); + if (!c.body.disableRedirect) c.setHeader("Location", url.toString()); + return c.json({ + url: url.toString(), + redirect: !c.body.disableRedirect + }); +}); +const signInEmail = () => createAuthEndpoint("/sign-in/email", { + method: "POST", + operationId: "signInEmail", + use: [formCsrfMiddleware], + body: z.object({ + email: z.string().meta({ description: "Email of the user" }), + password: z.string().meta({ description: "Password of the user" }), + callbackURL: z.string().meta({ description: "Callback URL to use as a redirect for email verification" }).optional(), + rememberMe: z.boolean().meta({ description: "If this is false, the session will not be remembered. Default is `true`." }).default(true).optional() + }), + metadata: { + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"], + $Infer: { + body: {}, + returned: {} + }, + openapi: { + operationId: "signInEmail", + description: "Sign in with email and password", + responses: { "200": { + description: "Success - Returns either session details or redirect URL", + content: { "application/json": { schema: { + type: "object", + description: "Session response when idToken is provided", + properties: { + redirect: { + type: "boolean", + enum: [false] + }, + token: { + type: "string", + description: "Session token" + }, + url: { + type: "string", + nullable: true + }, + user: { + type: "object", + $ref: "#/components/schemas/User" + } + }, + required: [ + "redirect", + "token", + "user" + ] + } } } + } } + } + } +}, async (ctx) => { + if (!ctx.context.options?.emailAndPassword?.enabled) { + ctx.context.logger.error("Email and password is not enabled. Make sure to enable it in the options on you `auth.ts` file. Check `https://better-auth.com/docs/authentication/email-password` for more!"); + throw new APIError("BAD_REQUEST", { message: "Email and password is not enabled" }); + } + const { email, password } = ctx.body; + if (!z.email().safeParse(email).success) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_EMAIL }); + const user = await ctx.context.internalAdapter.findUserByEmail(email, { includeAccounts: true }); + if (!user) { + await ctx.context.password.hash(password); + ctx.context.logger.error("User not found", { email }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + const credentialAccount = user.accounts.find((a) => a.providerId === "credential"); + if (!credentialAccount) { + await ctx.context.password.hash(password); + ctx.context.logger.error("Credential account not found", { email }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + const currentPassword = credentialAccount?.password; + if (!currentPassword) { + await ctx.context.password.hash(password); + ctx.context.logger.error("Password not found", { email }); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + if (!await ctx.context.password.verify({ + hash: currentPassword, + password + })) { + ctx.context.logger.error("Invalid password"); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD }); + } + if (ctx.context.options?.emailAndPassword?.requireEmailVerification && !user.user.emailVerified) { + if (!ctx.context.options?.emailVerification?.sendVerificationEmail) throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.EMAIL_NOT_VERIFIED }); + if (ctx.context.options?.emailVerification?.sendOnSignIn) { + const token = await createEmailVerificationToken(ctx.context.secret, user.user.email, void 0, ctx.context.options.emailVerification?.expiresIn); + const callbackURL = ctx.body.callbackURL ? encodeURIComponent(ctx.body.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: user.user, + url, + token + }, ctx.request)); + } + throw new APIError("FORBIDDEN", { message: BASE_ERROR_CODES.EMAIL_NOT_VERIFIED }); + } + const session = await ctx.context.internalAdapter.createSession(user.user.id, ctx.body.rememberMe === false); + if (!session) { + ctx.context.logger.error("Failed to create session"); + throw new APIError("UNAUTHORIZED", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + } + await setSessionCookie(ctx, { + session, + user: user.user + }, ctx.body.rememberMe === false); + if (ctx.body.callbackURL) ctx.setHeader("Location", ctx.body.callbackURL); + return ctx.json({ + redirect: !!ctx.body.callbackURL, + token: session.token, + url: ctx.body.callbackURL, + user: parseUserOutput(ctx.context.options, user.user) + }); +}); +const signOut = createAuthEndpoint("/sign-out", { + method: "POST", + operationId: "signOut", + requireHeaders: true, + metadata: { openapi: { + operationId: "signOut", + description: "Sign out the current user", + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { success: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const sessionCookieToken = await ctx.getSignedCookie(ctx.context.authCookies.sessionToken.name, ctx.context.secret); + if (sessionCookieToken) try { + await ctx.context.internalAdapter.deleteSession(sessionCookieToken); + } catch (e) { + ctx.context.logger.error("Failed to delete session from database", e); + } + deleteSessionCookie(ctx); + return ctx.json({ success: true }); +}); +const signUpEmailBodySchema = z.object({ + name: z.string(), + email: z.email(), + password: z.string().nonempty(), + image: z.string().optional(), + callbackURL: z.string().optional(), + rememberMe: z.boolean().optional() +}).and(z.record(z.string(), z.any())); +const signUpEmail = () => createAuthEndpoint("/sign-up/email", { + method: "POST", + operationId: "signUpWithEmailAndPassword", + use: [formCsrfMiddleware], + body: signUpEmailBodySchema, + metadata: { + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"], + $Infer: { + body: {}, + returned: {} + }, + openapi: { + operationId: "signUpWithEmailAndPassword", + description: "Sign up a user using email and password", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + name: { + type: "string", + description: "The name of the user" + }, + email: { + type: "string", + description: "The email of the user" + }, + password: { + type: "string", + description: "The password of the user" + }, + image: { + type: "string", + description: "The profile image URL of the user" + }, + callbackURL: { + type: "string", + description: "The URL to use for email verification callback" + }, + rememberMe: { + type: "boolean", + description: "If this is false, the session will not be remembered. Default is `true`." + } + }, + required: [ + "name", + "email", + "password" + ] + } } } }, + responses: { + "200": { + description: "Successfully created user", + content: { "application/json": { schema: { + type: "object", + properties: { + token: { + type: "string", + nullable: true, + description: "Authentication token for the session" + }, + user: { + type: "object", + properties: { + id: { + type: "string", + description: "The unique identifier of the user" + }, + email: { + type: "string", + format: "email", + description: "The email address of the user" + }, + name: { + type: "string", + description: "The name of the user" + }, + image: { + type: "string", + format: "uri", + nullable: true, + description: "The profile image URL of the user" + }, + emailVerified: { + type: "boolean", + description: "Whether the email has been verified" + }, + createdAt: { + type: "string", + format: "date-time", + description: "When the user was created" + }, + updatedAt: { + type: "string", + format: "date-time", + description: "When the user was last updated" + } + }, + required: [ + "id", + "email", + "name", + "emailVerified", + "createdAt", + "updatedAt" + ] + } + }, + required: ["user"] + } } } + }, + "422": { + description: "Unprocessable Entity. User already exists or failed to create user.", + content: { "application/json": { schema: { + type: "object", + properties: { message: { type: "string" } } + } } } + } + } + } + } +}, async (ctx) => { + return runWithTransaction(ctx.context.adapter, async () => { + if (!ctx.context.options.emailAndPassword?.enabled || ctx.context.options.emailAndPassword?.disableSignUp) throw new APIError("BAD_REQUEST", { message: "Email and password sign up is not enabled" }); + const body = ctx.body; + const { name, email, password, image, callbackURL: _callbackURL, rememberMe, ...rest } = body; + if (!z.email().safeParse(email).success) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_EMAIL }); + if (!password || typeof password !== "string") throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + const minPasswordLength = ctx.context.password.config.minPasswordLength; + if (password.length < minPasswordLength) { + ctx.context.logger.error("Password is too short"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + } + const maxPasswordLength = ctx.context.password.config.maxPasswordLength; + if (password.length > maxPasswordLength) { + ctx.context.logger.error("Password is too long"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + } + if ((await ctx.context.internalAdapter.findUserByEmail(email))?.user) { + ctx.context.logger.info(`Sign-up attempt for existing email: ${email}`); + throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL }); + } + const hash = await ctx.context.password.hash(password); + let createdUser; + try { + const data = parseUserInput(ctx.context.options, rest, "create"); + createdUser = await ctx.context.internalAdapter.createUser({ + email: email.toLowerCase(), + name, + image, + ...data, + emailVerified: false + }); + if (!createdUser) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER }); + } catch (e) { + if (isDevelopment()) ctx.context.logger.error("Failed to create user", e); + if (e instanceof APIError) throw e; + ctx.context.logger?.error("Failed to create user", e); + throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER }); + } + if (!createdUser) throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER }); + await ctx.context.internalAdapter.linkAccount({ + userId: createdUser.id, + providerId: "credential", + accountId: createdUser.id, + password: hash + }); + if (ctx.context.options.emailVerification?.sendOnSignUp ?? ctx.context.options.emailAndPassword.requireEmailVerification) { + const token = await createEmailVerificationToken(ctx.context.secret, createdUser.email, void 0, ctx.context.options.emailVerification?.expiresIn); + const callbackURL = body.callbackURL ? encodeURIComponent(body.callbackURL) : encodeURIComponent("/"); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; + if (ctx.context.options.emailVerification?.sendVerificationEmail) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: createdUser, + url, + token + }, ctx.request)); + } + if (ctx.context.options.emailAndPassword.autoSignIn === false || ctx.context.options.emailAndPassword.requireEmailVerification) return ctx.json({ + token: null, + user: parseUserOutput(ctx.context.options, createdUser) + }); + const session = await ctx.context.internalAdapter.createSession(createdUser.id, rememberMe === false); + if (!session) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }); + await setSessionCookie(ctx, { + session, + user: createdUser + }, rememberMe === false); + return ctx.json({ + token: session.token, + user: parseUserOutput(ctx.context.options, createdUser) + }); + }); +}); +const updateUserBodySchema = z.record(z.string().meta({ description: "Field name must be a string" }), z.any()); +const updateUser = () => createAuthEndpoint("/update-user", { + method: "POST", + operationId: "updateUser", + body: updateUserBodySchema, + use: [sessionMiddleware], + metadata: { + $Infer: { body: {} }, + openapi: { + operationId: "updateUser", + description: "Update the current user", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + name: { + type: "string", + description: "The name of the user" + }, + image: { + type: "string", + description: "The image of the user", + nullable: true + } + } + } } } }, + responses: { "200": { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { user: { + type: "object", + $ref: "#/components/schemas/User" + } } + } } } + } } + } + } +}, async (ctx) => { + const body = ctx.body; + if (typeof body !== "object" || Array.isArray(body)) throw new APIError("BAD_REQUEST", { message: "Body must be an object" }); + if (body.email) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.EMAIL_CAN_NOT_BE_UPDATED }); + const { name, image, ...rest } = body; + const session = ctx.context.session; + const additionalFields = parseUserInput(ctx.context.options, rest, "update"); + if (image === void 0 && name === void 0 && Object.keys(additionalFields).length === 0) throw new APIError("BAD_REQUEST", { message: "No fields to update" }); + const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, { + name, + image, + ...additionalFields + }) ?? { + ...session.user, + ...name !== void 0 && { name }, + ...image !== void 0 && { image }, + ...additionalFields + }; + await setSessionCookie(ctx, { + session: session.session, + user: updatedUser + }); + return ctx.json({ status: true }); +}); +const changePassword = createAuthEndpoint("/change-password", { + method: "POST", + operationId: "changePassword", + body: z.object({ + newPassword: z.string().meta({ description: "The new password to set" }), + currentPassword: z.string().meta({ description: "The current password is required" }), + revokeOtherSessions: z.boolean().meta({ description: "Must be a boolean value" }).optional() + }), + use: [sensitiveSessionMiddleware], + metadata: { openapi: { + operationId: "changePassword", + description: "Change the password of the user", + responses: { "200": { + description: "Password successfully changed", + content: { "application/json": { schema: { + type: "object", + properties: { + token: { + type: "string", + nullable: true, + description: "New session token if other sessions were revoked" + }, + user: { + type: "object", + properties: { + id: { + type: "string", + description: "The unique identifier of the user" + }, + email: { + type: "string", + format: "email", + description: "The email address of the user" + }, + name: { + type: "string", + description: "The name of the user" + }, + image: { + type: "string", + format: "uri", + nullable: true, + description: "The profile image URL of the user" + }, + emailVerified: { + type: "boolean", + description: "Whether the email has been verified" + }, + createdAt: { + type: "string", + format: "date-time", + description: "When the user was created" + }, + updatedAt: { + type: "string", + format: "date-time", + description: "When the user was last updated" + } + }, + required: [ + "id", + "email", + "name", + "emailVerified", + "createdAt", + "updatedAt" + ] + } + }, + required: ["user"] + } } } + } } + } } +}, async (ctx) => { + const { newPassword, currentPassword, revokeOtherSessions: revokeOtherSessions2 } = ctx.body; + const session = ctx.context.session; + const minPasswordLength = ctx.context.password.config.minPasswordLength; + if (newPassword.length < minPasswordLength) { + ctx.context.logger.error("Password is too short"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + } + const maxPasswordLength = ctx.context.password.config.maxPasswordLength; + if (newPassword.length > maxPasswordLength) { + ctx.context.logger.error("Password is too long"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + } + const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account$1) => account$1.providerId === "credential" && account$1.password); + if (!account || !account.password) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND }); + const passwordHash = await ctx.context.password.hash(newPassword); + if (!await ctx.context.password.verify({ + hash: account.password, + password: currentPassword + })) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + await ctx.context.internalAdapter.updateAccount(account.id, { password: passwordHash }); + let token = null; + if (revokeOtherSessions2) { + await ctx.context.internalAdapter.deleteSessions(session.user.id); + const newSession = await ctx.context.internalAdapter.createSession(session.user.id); + if (!newSession) throw new APIError("INTERNAL_SERVER_ERROR", { message: BASE_ERROR_CODES.FAILED_TO_GET_SESSION }); + await setSessionCookie(ctx, { + session: newSession, + user: session.user + }); + token = newSession.token; + } + return ctx.json({ + token, + user: parseUserOutput(ctx.context.options, session.user) + }); +}); +const setPassword = createAuthEndpoint({ + method: "POST", + body: z.object({ newPassword: z.string().meta({ description: "The new password to set is required" }) }), + use: [sensitiveSessionMiddleware] +}, async (ctx) => { + const { newPassword } = ctx.body; + const session = ctx.context.session; + const minPasswordLength = ctx.context.password.config.minPasswordLength; + if (newPassword.length < minPasswordLength) { + ctx.context.logger.error("Password is too short"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_SHORT }); + } + const maxPasswordLength = ctx.context.password.config.maxPasswordLength; + if (newPassword.length > maxPasswordLength) { + ctx.context.logger.error("Password is too long"); + throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.PASSWORD_TOO_LONG }); + } + const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account$1) => account$1.providerId === "credential" && account$1.password); + const passwordHash = await ctx.context.password.hash(newPassword); + if (!account) { + await ctx.context.internalAdapter.linkAccount({ + userId: session.user.id, + providerId: "credential", + accountId: session.user.id, + password: passwordHash + }); + return ctx.json({ status: true }); + } + throw new APIError("BAD_REQUEST", { message: "user already has a password" }); +}); +const deleteUser = createAuthEndpoint("/delete-user", { + method: "POST", + use: [sensitiveSessionMiddleware], + body: z.object({ + callbackURL: z.string().meta({ description: "The callback URL to redirect to after the user is deleted" }).optional(), + password: z.string().meta({ description: "The password of the user is required to delete the user" }).optional(), + token: z.string().meta({ description: "The token to delete the user is required" }).optional() + }), + metadata: { openapi: { + operationId: "deleteUser", + description: "Delete the user", + requestBody: { content: { "application/json": { schema: { + type: "object", + properties: { + callbackURL: { + type: "string", + description: "The callback URL to redirect to after the user is deleted" + }, + password: { + type: "string", + description: "The user's password. Required if session is not fresh" + }, + token: { + type: "string", + description: "The deletion verification token" + } + } + } } } }, + responses: { "200": { + description: "User deletion processed successfully", + content: { "application/json": { schema: { + type: "object", + properties: { + success: { + type: "boolean", + description: "Indicates if the operation was successful" + }, + message: { + type: "string", + enum: ["User deleted", "Verification email sent"], + description: "Status message of the deletion process" + } + }, + required: ["success", "message"] + } } } + } } + } } +}, async (ctx) => { + if (!ctx.context.options.user?.deleteUser?.enabled) { + ctx.context.logger.error("Delete user is disabled. Enable it in the options"); + throw new APIError("NOT_FOUND"); + } + const session = ctx.context.session; + if (ctx.body.password) { + const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account$1) => account$1.providerId === "credential" && account$1.password); + if (!account || !account.password) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND }); + if (!await ctx.context.password.verify({ + hash: account.password, + password: ctx.body.password + })) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD }); + } + if (ctx.body.token) { + await deleteUserCallback({ + ...ctx, + query: { token: ctx.body.token } + }); + return ctx.json({ + success: true, + message: "User deleted" + }); + } + if (ctx.context.options.user.deleteUser?.sendDeleteAccountVerification) { + const token = generateRandomString(32, "0-9", "a-z"); + await ctx.context.internalAdapter.createVerificationValue({ + value: session.user.id, + identifier: `delete-account-${token}`, + expiresAt: new Date(Date.now() + (ctx.context.options.user.deleteUser?.deleteTokenExpiresIn || 3600 * 24) * 1e3) + }); + const url = `${ctx.context.baseURL}/delete-user/callback?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.user.deleteUser.sendDeleteAccountVerification({ + user: session.user, + url, + token + }, ctx.request)); + return ctx.json({ + success: true, + message: "Verification email sent" + }); + } + if (!ctx.body.password && ctx.context.sessionConfig.freshAge !== 0) { + const currentAge = new Date(session.session.createdAt).getTime(); + const freshAge = ctx.context.sessionConfig.freshAge * 1e3; + if (Date.now() - currentAge > freshAge * 1e3) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.SESSION_EXPIRED }); + } + const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete; + if (beforeDelete) await beforeDelete(session.user, ctx.request); + await ctx.context.internalAdapter.deleteUser(session.user.id); + await ctx.context.internalAdapter.deleteSessions(session.user.id); + deleteSessionCookie(ctx); + const afterDelete = ctx.context.options.user.deleteUser?.afterDelete; + if (afterDelete) await afterDelete(session.user, ctx.request); + return ctx.json({ + success: true, + message: "User deleted" + }); +}); +const deleteUserCallback = createAuthEndpoint("/delete-user/callback", { + method: "GET", + query: z.object({ + token: z.string().meta({ description: "The token to verify the deletion request" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after deletion" }).optional() + }), + use: [originCheck((ctx) => ctx.query.callbackURL)], + metadata: { openapi: { + description: "Callback to complete user deletion with verification token", + responses: { "200": { + description: "User successfully deleted", + content: { "application/json": { schema: { + type: "object", + properties: { + success: { + type: "boolean", + description: "Indicates if the deletion was successful" + }, + message: { + type: "string", + enum: ["User deleted"], + description: "Confirmation message" + } + }, + required: ["success", "message"] + } } } + } } + } } +}, async (ctx) => { + if (!ctx.context.options.user?.deleteUser?.enabled) { + ctx.context.logger.error("Delete user is disabled. Enable it in the options"); + throw new APIError("NOT_FOUND"); + } + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO }); + const token = await ctx.context.internalAdapter.findVerificationValue(`delete-account-${ctx.query.token}`); + if (!token || token.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + if (token.value !== session.user.id) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.INVALID_TOKEN }); + const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete; + if (beforeDelete) await beforeDelete(session.user, ctx.request); + await ctx.context.internalAdapter.deleteUser(session.user.id); + await ctx.context.internalAdapter.deleteSessions(session.user.id); + await ctx.context.internalAdapter.deleteAccounts(session.user.id); + await ctx.context.internalAdapter.deleteVerificationValue(token.id); + deleteSessionCookie(ctx); + const afterDelete = ctx.context.options.user.deleteUser?.afterDelete; + if (afterDelete) await afterDelete(session.user, ctx.request); + if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL || "/"); + return ctx.json({ + success: true, + message: "User deleted" + }); +}); +const changeEmail = createAuthEndpoint("/change-email", { + method: "POST", + body: z.object({ + newEmail: z.email().meta({ description: "The new email address to set must be a valid email address" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional() + }), + use: [sensitiveSessionMiddleware], + metadata: { openapi: { + operationId: "changeEmail", + responses: { + "200": { + description: "Email change request processed successfully", + content: { "application/json": { schema: { + type: "object", + properties: { + user: { + type: "object", + $ref: "#/components/schemas/User" + }, + status: { + type: "boolean", + description: "Indicates if the request was successful" + }, + message: { + type: "string", + enum: ["Email updated", "Verification email sent"], + description: "Status message of the email change process", + nullable: true + } + }, + required: ["status"] + } } } + }, + "422": { + description: "Unprocessable Entity. Email already exists", + content: { "application/json": { schema: { + type: "object", + properties: { message: { type: "string" } } + } } } + } + } + } } +}, async (ctx) => { + if (!ctx.context.options.user?.changeEmail?.enabled) { + ctx.context.logger.error("Change email is disabled."); + throw new APIError("BAD_REQUEST", { message: "Change email is disabled" }); + } + const newEmail = ctx.body.newEmail.toLowerCase(); + if (newEmail === ctx.context.session.user.email) { + ctx.context.logger.error("Email is the same"); + throw new APIError("BAD_REQUEST", { message: "Email is the same" }); + } + if (await ctx.context.internalAdapter.findUserByEmail(newEmail)) { + ctx.context.logger.error("Email already exists"); + throw new APIError("UNPROCESSABLE_ENTITY", { message: BASE_ERROR_CODES.USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL }); + } + if (ctx.context.session.user.emailVerified !== true && ctx.context.options.user.changeEmail.updateEmailWithoutVerification) { + await ctx.context.internalAdapter.updateUserByEmail(ctx.context.session.user.email, { email: newEmail }); + await setSessionCookie(ctx, { + session: ctx.context.session.session, + user: { + ...ctx.context.session.user, + email: newEmail + } + }); + if (ctx.context.options.emailVerification?.sendVerificationEmail) { + const token$1 = await createEmailVerificationToken(ctx.context.secret, newEmail, void 0, ctx.context.options.emailVerification?.expiresIn); + const url$1 = `${ctx.context.baseURL}/verify-email?token=${token$1}&callbackURL=${ctx.body.callbackURL || "/"}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: { + ...ctx.context.session.user, + email: newEmail + }, + url: url$1, + token: token$1 + }, ctx.request)); + } + return ctx.json({ status: true }); + } + if (ctx.context.session.user.emailVerified && (ctx.context.options.user.changeEmail.sendChangeEmailConfirmation || ctx.context.options.user.changeEmail.sendChangeEmailVerification)) { + const token$1 = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-confirmation" }); + const url$1 = `${ctx.context.baseURL}/verify-email?token=${token$1}&callbackURL=${ctx.body.callbackURL || "/"}`; + const sendFn = ctx.context.options.user.changeEmail.sendChangeEmailConfirmation || ctx.context.options.user.changeEmail.sendChangeEmailVerification; + if (sendFn) await ctx.context.runInBackgroundOrAwait(sendFn({ + user: ctx.context.session.user, + newEmail, + url: url$1, + token: token$1 + }, ctx.request)); + return ctx.json({ status: true }); + } + if (!ctx.context.options.emailVerification?.sendVerificationEmail) { + ctx.context.logger.error("Verification email isn't enabled."); + throw new APIError("BAD_REQUEST", { message: "Verification email isn't enabled" }); + } + const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-verification" }); + const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`; + await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({ + user: { + ...ctx.context.session.user, + email: newEmail + }, + url, + token + }, ctx.request)); + return ctx.json({ status: true }); +}); +const defuReplaceArrays = createDefu((obj, key, value) => { + if (Array.isArray(obj[key]) && Array.isArray(value)) { + obj[key] = value; + return true; + } +}); +const hooksSourceWeakMap = /* @__PURE__ */ new WeakMap(); +function toAuthEndpoints(endpoints, ctx) { + const api = {}; + for (const [key, endpoint] of Object.entries(endpoints)) { + api[key] = async (context) => { + const run = async () => { + const authContext = await ctx; + let internalContext = { + ...context, + context: { + ...authContext, + returned: void 0, + responseHeaders: void 0, + session: null + }, + path: endpoint.path, + headers: context?.headers ? new Headers(context?.headers) : void 0 + }; + return runWithEndpointContext(internalContext, async () => { + const { beforeHooks, afterHooks } = getHooks(authContext); + const before = await runBeforeHooks(internalContext, beforeHooks); + if ("context" in before && before.context && typeof before.context === "object") { + const { headers, ...rest } = before.context; + if (headers) headers.forEach((value, key$1) => { + internalContext.headers.set(key$1, value); + }); + internalContext = defuReplaceArrays(rest, internalContext); + } else if (before) return context?.asResponse ? toResponse(before, { headers: context?.headers }) : context?.returnHeaders ? { + headers: context?.headers, + response: before + } : before; + internalContext.asResponse = false; + internalContext.returnHeaders = true; + internalContext.returnStatus = true; + const result = await runWithEndpointContext(internalContext, () => endpoint(internalContext)).catch((e) => { + if (e instanceof APIError) + return { + response: e, + status: e.statusCode, + headers: e.headers ? new Headers(e.headers) : null + }; + throw e; + }); + if (result && result instanceof Response) return result; + internalContext.context.returned = result.response; + internalContext.context.responseHeaders = result.headers; + const after = await runAfterHooks(internalContext, afterHooks); + if (after.response) result.response = after.response; + if (result.response instanceof APIError && shouldPublishLog(authContext.logger.level, "debug")) result.response.stack = result.response.errorStack; + if (result.response instanceof APIError && !context?.asResponse) throw result.response; + return context?.asResponse ? toResponse(result.response, { + headers: result.headers, + status: result.status + }) : context?.returnHeaders ? context?.returnStatus ? { + headers: result.headers, + response: result.response, + status: result.status + } : { + headers: result.headers, + response: result.response + } : context?.returnStatus ? { + response: result.response, + status: result.status + } : result.response; + }); + }; + if (await hasRequestState()) return run(); + else return runWithRequestState(/* @__PURE__ */ new WeakMap(), run); + }; + api[key].path = endpoint.path; + api[key].options = endpoint.options; + } + return api; +} +async function runBeforeHooks(context, hooks) { + let modifiedContext = {}; + for (const hook of hooks) { + let matched = false; + try { + matched = hook.matcher(context); + } catch (error2) { + const hookSource = hooksSourceWeakMap.get(hook.handler) ?? "unknown"; + context.context.logger.error(`An error occurred during ${hookSource} hook matcher execution:`, error2); + throw new APIError("INTERNAL_SERVER_ERROR", { message: `An error occurred during hook matcher execution. Check the logs for more details.` }); + } + if (matched) { + const result = await hook.handler({ + ...context, + returnHeaders: false + }).catch((e) => { + if (e instanceof APIError && shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack; + throw e; + }); + if (result && typeof result === "object") { + if ("context" in result && typeof result.context === "object") { + const { headers, ...rest } = result.context; + if (headers instanceof Headers) if (modifiedContext.headers) headers.forEach((value, key) => { + modifiedContext.headers?.set(key, value); + }); + else modifiedContext.headers = headers; + modifiedContext = defuReplaceArrays(rest, modifiedContext); + continue; + } + return result; + } + } + } + return { context: modifiedContext }; +} +async function runAfterHooks(context, hooks) { + for (const hook of hooks) if (hook.matcher(context)) { + const result = await hook.handler(context).catch((e) => { + if (e instanceof APIError) { + if (shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack; + return { + response: e, + headers: e.headers ? new Headers(e.headers) : null + }; + } + throw e; + }); + if (result.headers) result.headers.forEach((value, key) => { + if (!context.context.responseHeaders) context.context.responseHeaders = new Headers({ [key]: value }); + else if (key.toLowerCase() === "set-cookie") context.context.responseHeaders.append(key, value); + else context.context.responseHeaders.set(key, value); + }); + if (result.response) context.context.returned = result.response; + } + return { + response: context.context.returned, + headers: context.context.responseHeaders + }; +} +function getHooks(authContext) { + const plugins = authContext.options.plugins || []; + const beforeHooks = []; + const afterHooks = []; + const beforeHookHandler = authContext.options.hooks?.before; + if (beforeHookHandler) { + hooksSourceWeakMap.set(beforeHookHandler, "user"); + beforeHooks.push({ + matcher: () => true, + handler: beforeHookHandler + }); + } + const afterHookHandler = authContext.options.hooks?.after; + if (afterHookHandler) { + hooksSourceWeakMap.set(afterHookHandler, "user"); + afterHooks.push({ + matcher: () => true, + handler: afterHookHandler + }); + } + const pluginBeforeHooks = plugins.filter((plugin) => plugin.hooks?.before).map((plugin) => plugin.hooks?.before).flat(); + const pluginAfterHooks = plugins.filter((plugin) => plugin.hooks?.after).map((plugin) => plugin.hooks?.after).flat(); + if (pluginBeforeHooks.length) beforeHooks.push(...pluginBeforeHooks); + if (pluginAfterHooks.length) afterHooks.push(...pluginAfterHooks); + return { + beforeHooks, + afterHooks + }; +} +function checkEndpointConflicts(options, logger$1) { + const endpointRegistry = /* @__PURE__ */ new Map(); + options.plugins?.forEach((plugin) => { + if (plugin.endpoints) { + for (const [key, endpoint] of Object.entries(plugin.endpoints)) if (endpoint && "path" in endpoint && typeof endpoint.path === "string") { + const path = endpoint.path; + let methods = []; + if (endpoint.options && "method" in endpoint.options) { + if (Array.isArray(endpoint.options.method)) methods = endpoint.options.method; + else if (typeof endpoint.options.method === "string") methods = [endpoint.options.method]; + } + if (methods.length === 0) methods = ["*"]; + if (!endpointRegistry.has(path)) endpointRegistry.set(path, []); + endpointRegistry.get(path).push({ + pluginId: plugin.id, + endpointKey: key, + methods + }); + } + } + }); + const conflicts = []; + for (const [path, entries] of endpointRegistry.entries()) if (entries.length > 1) { + const methodMap = /* @__PURE__ */ new Map(); + let hasConflict = false; + for (const entry of entries) for (const method of entry.methods) { + if (!methodMap.has(method)) methodMap.set(method, []); + methodMap.get(method).push(entry.pluginId); + if (methodMap.get(method).length > 1) hasConflict = true; + if (method === "*" && entries.length > 1) hasConflict = true; + else if (method !== "*" && methodMap.has("*")) hasConflict = true; + } + if (hasConflict) { + const uniquePlugins = [...new Set(entries.map((e) => e.pluginId))]; + const conflictingMethods = []; + for (const [method, plugins] of methodMap.entries()) if (plugins.length > 1 || method === "*" && entries.length > 1 || method !== "*" && methodMap.has("*")) conflictingMethods.push(method); + conflicts.push({ + path, + plugins: uniquePlugins, + conflictingMethods + }); + } + } + if (conflicts.length > 0) { + const conflictMessages = conflicts.map((conflict) => ` - "${conflict.path}" [${conflict.conflictingMethods.join(", ")}] used by plugins: ${conflict.plugins.join(", ")}`).join("\n"); + logger$1.error(`Endpoint path conflicts detected! Multiple plugins are trying to use the same endpoint paths with conflicting HTTP methods: +${conflictMessages} + +To resolve this, you can: + 1. Use only one of the conflicting plugins + 2. Configure the plugins to use different paths (if supported) + 3. Ensure plugins use different HTTP methods for the same path +`); + } +} +function getEndpoints(ctx, options) { + const pluginEndpoints = options.plugins?.reduce((acc, plugin) => { + return { + ...acc, + ...plugin.endpoints + }; + }, {}) ?? {}; + const middlewares = options.plugins?.map((plugin) => plugin.middlewares?.map((m) => { + const middleware = (async (context) => { + const authContext = await ctx; + return m.middleware({ + ...context, + context: { + ...authContext, + ...context.context + } + }); + }); + middleware.options = m.middleware.options; + return { + path: m.path, + middleware + }; + })).filter((plugin) => plugin !== void 0).flat() || []; + return { + api: toAuthEndpoints({ + signInSocial: signInSocial(), + callbackOAuth, + getSession: getSession(), + signOut, + signUpEmail: signUpEmail(), + signInEmail: signInEmail(), + resetPassword, + verifyPassword, + verifyEmail, + sendVerificationEmail, + changeEmail, + changePassword, + setPassword, + updateUser: updateUser(), + deleteUser, + requestPasswordReset, + requestPasswordResetCallback, + listSessions: listSessions(), + revokeSession, + revokeSessions, + revokeOtherSessions, + linkSocialAccount, + listUserAccounts, + deleteUserCallback, + unlinkAccount, + refreshToken, + getAccessToken, + accountInfo, + ...pluginEndpoints, + ok, + error + }, ctx), + middlewares + }; +} +const router = (ctx, options) => { + const { api, middlewares } = getEndpoints(ctx, options); + const basePath = new URL(ctx.baseURL).pathname; + return createRouter(api, { + routerContext: ctx, + openapi: { disabled: true }, + basePath, + routerMiddleware: [{ + path: "/**", + middleware: originCheckMiddleware + }, ...middlewares], + allowedMediaTypes: ["application/json"], + skipTrailingSlashes: options.advanced?.skipTrailingSlashes ?? false, + async onRequest(req) { + const disabledPaths = ctx.options.disabledPaths || []; + const normalizedPath = normalizePathname(req.url, basePath); + if (disabledPaths.includes(normalizedPath)) return new Response("Not Found", { status: 404 }); + let currentRequest = req; + for (const plugin of ctx.options.plugins || []) if (plugin.onRequest) { + const response = await plugin.onRequest(currentRequest, ctx); + if (response && "response" in response) return response.response; + if (response && "request" in response) currentRequest = response.request; + } + const rateLimitResponse2 = await onRequestRateLimit(currentRequest, ctx); + if (rateLimitResponse2) return rateLimitResponse2; + return currentRequest; + }, + async onResponse(res) { + for (const plugin of ctx.options.plugins || []) if (plugin.onResponse) { + const response = await plugin.onResponse(res, ctx); + if (response) return response.response; + } + return res; + }, + onError(e) { + if (e instanceof APIError && e.status === "FOUND") return; + if (options.onAPIError?.throw) throw e; + if (options.onAPIError?.onError) { + options.onAPIError.onError(e, ctx); + return; + } + const optLogLevel = options.logger?.level; + const log = optLogLevel === "error" || optLogLevel === "warn" || optLogLevel === "debug" ? logger : void 0; + if (options.logger?.disabled !== true) { + if (e && typeof e === "object" && "message" in e && typeof e.message === "string") { + if (e.message.includes("no column") || e.message.includes("column") || e.message.includes("relation") || e.message.includes("table") || e.message.includes("does not exist")) { + ctx.logger?.error(e.message); + return; + } + } + if (e instanceof APIError) { + if (e.status === "INTERNAL_SERVER_ERROR") ctx.logger.error(e.status, e); + log?.error(e.message); + } else ctx.logger?.error(e && typeof e === "object" && "name" in e ? e.name : "", e); + } + } + }); +}; +async function runPluginInit(ctx) { + let options = ctx.options; + const plugins = options.plugins || []; + let context = ctx; + const dbHooks = []; + for (const plugin of plugins) if (plugin.init) { + const initPromise = plugin.init(context); + let result; + if (isPromise(initPromise)) result = await initPromise; + else result = initPromise; + if (typeof result === "object") { + if (result.options) { + const { databaseHooks, ...restOpts } = result.options; + if (databaseHooks) dbHooks.push(databaseHooks); + options = defu(options, restOpts); + } + if (result.context) context = { + ...context, + ...result.context + }; + } + } + dbHooks.push(options.databaseHooks); + context.internalAdapter = createInternalAdapter(context.adapter, { + options, + logger: context.logger, + hooks: dbHooks.filter((u) => u !== void 0), + generateId: context.generateId + }); + context.options = options; + return { context }; +} +function getInternalPlugins(options) { + const plugins = []; + if (options.advanced?.crossSubDomainCookies?.enabled) ; + return plugins; +} +async function getTrustedOrigins(options, request) { + const baseURL = getBaseURL(options.baseURL, options.basePath, request); + const trustedOrigins = baseURL ? [new URL(baseURL).origin] : []; + if (options.trustedOrigins) { + if (Array.isArray(options.trustedOrigins)) trustedOrigins.push(...options.trustedOrigins); + if (typeof options.trustedOrigins === "function") { + const validOrigins = await options.trustedOrigins(request); + trustedOrigins.push(...validOrigins); + } + } + const envTrustedOrigins = env.BETTER_AUTH_TRUSTED_ORIGINS; + if (envTrustedOrigins) trustedOrigins.push(...envTrustedOrigins.split(",")); + return trustedOrigins.filter((v) => Boolean(v)); +} +const createBetterAuth = (options, initFn) => { + const authContext = initFn(options); + const { api } = getEndpoints(authContext, options); + return { + handler: async (request) => { + const ctx = await authContext; + const basePath = ctx.options.basePath || "/api/auth"; + if (!ctx.options.baseURL) { + const baseURL = getBaseURL(void 0, basePath, request, void 0, ctx.options.advanced?.trustedProxyHeaders); + if (baseURL) { + ctx.baseURL = baseURL; + ctx.options.baseURL = getOrigin(ctx.baseURL) || void 0; + } else throw new BetterAuthError("Could not get base URL from request. Please provide a valid base URL."); + } + ctx.trustedOrigins = await getTrustedOrigins(ctx.options, request); + const { handler } = router(ctx, options); + return runWithAdapter(ctx.adapter, () => handler(request)); + }, + api, + options, + $context: authContext, + $ERROR_CODES: { + ...options.plugins?.reduce((acc, plugin) => { + if (plugin.$ERROR_CODES) return { + ...acc, + ...plugin.$ERROR_CODES + }; + return acc; + }, {}), + ...BASE_ERROR_CODES + } + }; +}; +const DEFAULT_SECRET = "better-auth-secret-12345678901234567890"; +function estimateEntropy(str) { + const unique = new Set(str).size; + if (unique === 0) return 0; + return Math.log2(Math.pow(unique, str.length)); +} +function validateSecret(secret, logger$1) { + const isDefaultSecret = secret === DEFAULT_SECRET; + if (isTest()) return; + if (isDefaultSecret && isProduction) throw new BetterAuthError("You are using the default secret. Please set `BETTER_AUTH_SECRET` in your environment variables or pass `secret` in your auth config."); + if (secret.length < 32) logger$1.warn(`[better-auth] Warning: your BETTER_AUTH_SECRET should be at least 32 characters long for adequate security. Generate one with \`npx @better-auth/cli secret\` or \`openssl rand -base64 32\`.`); + if (estimateEntropy(secret) < 120) logger$1.warn("[better-auth] Warning: your BETTER_AUTH_SECRET appears low-entropy. Use a randomly generated secret for production."); +} +async function createAuthContext(adapter, options, getDatabaseType) { + if (!options.database) options = defu$1(options, { + session: { cookieCache: { + enabled: true, + strategy: "jwe", + refreshCache: true + } }, + account: { + storeStateStrategy: "cookie", + storeAccountCookie: true + } + }); + const plugins = options.plugins || []; + const internalPlugins = getInternalPlugins(options); + const logger$1 = createLogger(options.logger); + const baseURL = getBaseURL(options.baseURL, options.basePath); + if (!baseURL) logger$1.warn(`[better-auth] Base URL could not be determined. Please set a valid base URL using the baseURL config option or the BETTER_AUTH_URL environment variable. Without this, callbacks and redirects may not work correctly.`); + if (adapter.id === "memory" && options.advanced?.database?.generateId === false) logger$1.error(`[better-auth] Misconfiguration detected. +You are using the memory DB with generateId: false. +This will cause no id to be generated for any model. +Most of the features of Better Auth will not work correctly.`); + const secret = options.secret || env.BETTER_AUTH_SECRET || env.AUTH_SECRET || DEFAULT_SECRET; + validateSecret(secret, logger$1); + options = { + ...options, + secret, + baseURL: baseURL ? new URL(baseURL).origin : "", + basePath: options.basePath || "/api/auth", + plugins: plugins.concat(internalPlugins) + }; + checkEndpointConflicts(options, logger$1); + const cookies = getCookies(options); + const tables = getAuthTables(options); + const providers = Object.entries(options.socialProviders || {}).map(([key, config2]) => { + if (config2 == null) return null; + if (config2.enabled === false) return null; + if (!config2.clientId) logger$1.warn(`Social provider ${key} is missing clientId or clientSecret`); + const provider = socialProviders[key](config2); + provider.disableImplicitSignUp = config2.disableImplicitSignUp; + return provider; + }).filter((x) => x !== null); + const generateIdFunc = ({ model, size }) => { + if (typeof options.advanced?.generateId === "function") return options.advanced.generateId({ + model, + size + }); + const dbGenerateId = options?.advanced?.database?.generateId; + if (typeof dbGenerateId === "function") return dbGenerateId({ + model, + size + }); + if (dbGenerateId === "uuid") return crypto.randomUUID(); + if (dbGenerateId === "serial" || dbGenerateId === false) return false; + return generateId(size); + }; + const { publish } = await createTelemetry(options, { + adapter: adapter.id, + database: typeof options.database === "function" ? "adapter" : getDatabaseType(options.database) + }); + const trustedOrigins = await getTrustedOrigins(options); + const initOrPromise = runPluginInit({ + appName: options.appName || "Better Auth", + baseURL: baseURL || "", + version: getBetterAuthVersion(), + socialProviders: providers, + options, + oauthConfig: { + storeStateStrategy: options.account?.storeStateStrategy || (options.database ? "database" : "cookie"), + skipStateCookieCheck: !!options.account?.skipStateCookieCheck + }, + tables, + trustedOrigins, + isTrustedOrigin(url, settings) { + return this.trustedOrigins.some((origin) => matchesOriginPattern(url, origin, settings)); + }, + sessionConfig: { + updateAge: options.session?.updateAge !== void 0 ? options.session.updateAge : 1440 * 60, + expiresIn: options.session?.expiresIn || 3600 * 24 * 7, + freshAge: options.session?.freshAge === void 0 ? 3600 * 24 : options.session.freshAge, + cookieRefreshCache: (() => { + const refreshCache = options.session?.cookieCache?.refreshCache; + const maxAge = options.session?.cookieCache?.maxAge || 300; + if ((!!options.database || !!options.secondaryStorage) && refreshCache) { + logger$1.warn("[better-auth] `session.cookieCache.refreshCache` is enabled while `database` or `secondaryStorage` is configured. `refreshCache` is meant for stateless (DB-less) setups. Disabling `refreshCache` — remove it from your config to silence this warning."); + return false; + } + if (refreshCache === false || refreshCache === void 0) return false; + if (refreshCache === true) return { + enabled: true, + updateAge: Math.floor(maxAge * 0.2) + }; + return { + enabled: true, + updateAge: refreshCache.updateAge !== void 0 ? refreshCache.updateAge : Math.floor(maxAge * 0.2) + }; + })() + }, + secret, + rateLimit: { + ...options.rateLimit, + enabled: options.rateLimit?.enabled ?? isProduction, + window: options.rateLimit?.window || 10, + max: options.rateLimit?.max || 100, + storage: options.rateLimit?.storage || (options.secondaryStorage ? "secondary-storage" : "memory") + }, + authCookies: cookies, + logger: logger$1, + generateId: generateIdFunc, + session: null, + secondaryStorage: options.secondaryStorage, + password: { + hash: options.emailAndPassword?.password?.hash || hashPassword, + verify: options.emailAndPassword?.password?.verify || verifyPassword$1, + config: { + minPasswordLength: options.emailAndPassword?.minPasswordLength || 8, + maxPasswordLength: options.emailAndPassword?.maxPasswordLength || 128 + }, + checkPassword + }, + setNewSession(session) { + this.newSession = session; + }, + newSession: null, + adapter, + internalAdapter: createInternalAdapter(adapter, { + options, + logger: logger$1, + hooks: options.databaseHooks ? [options.databaseHooks] : [], + generateId: generateIdFunc + }), + createAuthCookie: createCookieGetter(options), + async runMigrations() { + throw new BetterAuthError("runMigrations will be set by the specific init implementation"); + }, + publishTelemetry: publish, + skipCSRFCheck: !!options.advanced?.disableCSRFCheck, + skipOriginCheck: options.advanced?.disableOriginCheck !== void 0 ? options.advanced.disableOriginCheck : isTest() ? true : false, + runInBackground: options.advanced?.backgroundTasks?.handler ?? ((p) => { + p.catch(() => { + }); + }), + async runInBackgroundOrAwait(promise) { + try { + if (options.advanced?.backgroundTasks?.handler) { + if (promise instanceof Promise) options.advanced.backgroundTasks.handler(promise.catch((e) => { + logger$1.error("Failed to run background task:", e); + })); + } else await promise; + } catch (e) { + logger$1.error("Failed to run background task:", e); + } + }, + getPlugin: (id) => options.plugins.find((p) => p.id === id) ?? null + }); + let context; + if (isPromise(initOrPromise)) ({ context } = await initOrPromise); + else ({ context } = initOrPromise); + if (typeof context.options.emailVerification?.onEmailVerification === "function") context.options.emailVerification.onEmailVerification = deprecate(context.options.emailVerification.onEmailVerification, "Use `afterEmailVerification` instead. This will be removed in 1.5", context.logger); + return context; +} +const initMinimal = async (options) => { + const adapter = await getBaseAdapter(options, async () => { + throw new BetterAuthError("Direct database connection requires Kysely. Please use `better-auth` instead of `better-auth/minimal`, or provide an adapter (drizzleAdapter, prismaAdapter, etc.)"); + }); + const getDatabaseType = (_database) => "unknown"; + const ctx = await createAuthContext(adapter, options, getDatabaseType); + ctx.runMigrations = async function() { + throw new BetterAuthError("Migrations are not supported in 'better-auth/minimal'. Please use 'better-auth' for migration support."); + }; + return ctx; +}; +const betterAuth = (options) => { + return createBetterAuth(options, initMinimal); +}; +const drizzleAdapter = (db2, config2) => { + let lazyOptions = null; + const createCustomAdapter = (db$1) => ({ getFieldName, getDefaultFieldName, options }) => { + function getSchema2(model) { + const schema2 = config2.schema || db$1._.fullSchema; + if (!schema2) throw new BetterAuthError("Drizzle adapter failed to initialize. Schema not found. Please provide a schema object in the adapter options object."); + const schemaModel = schema2[model]; + if (!schemaModel) throw new BetterAuthError(`[# Drizzle Adapter]: The model "${model}" was not found in the schema object. Please pass the schema directly to the adapter options.`); + return schemaModel; + } + const withReturning = async (model, builder, data, where) => { + if (config2.provider !== "mysql") return (await builder.returning())[0]; + await builder.execute(); + const schemaModel = getSchema2(model); + const builderVal = builder.config?.values; + if (where?.length) { + const clause = convertWhereClause(where.map((w) => { + if (data[w.field] !== void 0) return { + ...w, + value: data[w.field] + }; + return w; + }), model); + return (await db$1.select().from(schemaModel).where(...clause))[0]; + } else if (builderVal && builderVal[0]?.id?.value) { + let tId = builderVal[0]?.id?.value; + if (!tId) tId = (await db$1.select({ id: sql$1`LAST_INSERT_ID()` }).from(schemaModel).orderBy(desc(schemaModel.id)).limit(1))[0].id; + return (await db$1.select().from(schemaModel).where(eq(schemaModel.id, tId)).limit(1).execute())[0]; + } else if (data.id) return (await db$1.select().from(schemaModel).where(eq(schemaModel.id, data.id)).limit(1).execute())[0]; + else { + if (!("id" in schemaModel)) throw new BetterAuthError(`The model "${model}" does not have an "id" field. Please use the "id" field as your primary key.`); + return (await db$1.select().from(schemaModel).orderBy(desc(schemaModel.id)).limit(1).execute())[0]; + } + }; + function convertWhereClause(where, model) { + const schemaModel = getSchema2(model); + if (!where) return []; + if (where.length === 1) { + const w = where[0]; + if (!w) return []; + const field = getFieldName({ + model, + field: w.field + }); + if (!schemaModel[field]) throw new BetterAuthError(`The field "${w.field}" does not exist in the schema for the model "${model}". Please update your schema.`); + if (w.operator === "in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`); + return [inArray(schemaModel[field], w.value)]; + } + if (w.operator === "not_in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "not_in" operator.`); + return [notInArray(schemaModel[field], w.value)]; + } + if (w.operator === "contains") return [like(schemaModel[field], `%${w.value}%`)]; + if (w.operator === "starts_with") return [like(schemaModel[field], `${w.value}%`)]; + if (w.operator === "ends_with") return [like(schemaModel[field], `%${w.value}`)]; + if (w.operator === "lt") return [lt(schemaModel[field], w.value)]; + if (w.operator === "lte") return [lte(schemaModel[field], w.value)]; + if (w.operator === "ne") return [ne(schemaModel[field], w.value)]; + if (w.operator === "gt") return [gt(schemaModel[field], w.value)]; + if (w.operator === "gte") return [gte(schemaModel[field], w.value)]; + return [eq(schemaModel[field], w.value)]; + } + const andGroup = where.filter((w) => w.connector === "AND" || !w.connector); + const orGroup = where.filter((w) => w.connector === "OR"); + const andClause = and(...andGroup.map((w) => { + const field = getFieldName({ + model, + field: w.field + }); + if (w.operator === "in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`); + return inArray(schemaModel[field], w.value); + } + if (w.operator === "not_in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "not_in" operator.`); + return notInArray(schemaModel[field], w.value); + } + if (w.operator === "contains") return like(schemaModel[field], `%${w.value}%`); + if (w.operator === "starts_with") return like(schemaModel[field], `${w.value}%`); + if (w.operator === "ends_with") return like(schemaModel[field], `%${w.value}`); + if (w.operator === "lt") return lt(schemaModel[field], w.value); + if (w.operator === "lte") return lte(schemaModel[field], w.value); + if (w.operator === "gt") return gt(schemaModel[field], w.value); + if (w.operator === "gte") return gte(schemaModel[field], w.value); + if (w.operator === "ne") return ne(schemaModel[field], w.value); + return eq(schemaModel[field], w.value); + })); + const orClause = or(...orGroup.map((w) => { + const field = getFieldName({ + model, + field: w.field + }); + if (w.operator === "in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`); + return inArray(schemaModel[field], w.value); + } + if (w.operator === "not_in") { + if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "not_in" operator.`); + return notInArray(schemaModel[field], w.value); + } + if (w.operator === "contains") return like(schemaModel[field], `%${w.value}%`); + if (w.operator === "starts_with") return like(schemaModel[field], `${w.value}%`); + if (w.operator === "ends_with") return like(schemaModel[field], `%${w.value}`); + if (w.operator === "lt") return lt(schemaModel[field], w.value); + if (w.operator === "lte") return lte(schemaModel[field], w.value); + if (w.operator === "gt") return gt(schemaModel[field], w.value); + if (w.operator === "gte") return gte(schemaModel[field], w.value); + if (w.operator === "ne") return ne(schemaModel[field], w.value); + return eq(schemaModel[field], w.value); + })); + const clause = []; + if (andGroup.length) clause.push(andClause); + if (orGroup.length) clause.push(orClause); + return clause; + } + function checkMissingFields(schema2, model, values) { + if (!schema2) throw new BetterAuthError("Drizzle adapter failed to initialize. Drizzle Schema not found. Please provide a schema object in the adapter options object."); + for (const key in values) if (!schema2[key]) throw new BetterAuthError(`The field "${key}" does not exist in the "${model}" Drizzle schema. Please update your drizzle schema or re-generate using "npx @better-auth/cli@latest generate".`); + } + return { + async create({ model, data: values }) { + const schemaModel = getSchema2(model); + checkMissingFields(schemaModel, model, values); + return await withReturning(model, db$1.insert(schemaModel).values(values), values); + }, + async findOne({ model, where, select, join }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + if (options.experimental?.joins) if (!db$1.query || !db$1.query[model]) { + logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`); + logger.info("Falling back to regular query"); + } else { + let includes; + const pluralJoinResults = []; + if (join) { + includes = {}; + const joinEntries = Object.entries(join); + for (const [model$1, joinAttr] of joinEntries) { + const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100; + const isUnique = joinAttr.relation === "one-to-one"; + const pluralSuffix = isUnique || config2.usePlural ? "" : "s"; + includes[`${model$1}${pluralSuffix}`] = isUnique ? true : { limit }; + if (!isUnique) pluralJoinResults.push(`${model$1}${pluralSuffix}`); + } + } + const res$1 = await db$1.query[model].findFirst({ + where: clause[0], + columns: select?.length && select.length > 0 ? select.reduce((acc, field) => { + acc[getFieldName({ + model, + field + })] = true; + return acc; + }, {}) : void 0, + with: includes + }); + if (res$1) for (const pluralJoinResult of pluralJoinResults) { + const singularKey = !config2.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult; + res$1[singularKey] = res$1[pluralJoinResult]; + if (pluralJoinResult !== singularKey) delete res$1[pluralJoinResult]; + } + return res$1; + } + const res = await db$1.select(select?.length && select.length > 0 ? select.reduce((acc, field) => { + const fieldName = getFieldName({ + model, + field + }); + return { + ...acc, + [fieldName]: schemaModel[fieldName] + }; + }, {}) : void 0).from(schemaModel).where(...clause); + if (!res.length) return null; + return res[0]; + }, + async findMany({ model, where, sortBy, limit, select, offset, join }) { + const schemaModel = getSchema2(model); + const clause = where ? convertWhereClause(where, model) : []; + const sortFn = sortBy?.direction === "desc" ? desc : asc; + if (options.experimental?.joins) if (!db$1.query[model]) { + logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`); + logger.info("Falling back to regular query"); + } else { + let includes; + const pluralJoinResults = []; + if (join) { + includes = {}; + const joinEntries = Object.entries(join); + for (const [model$1, joinAttr] of joinEntries) { + const isUnique = joinAttr.relation === "one-to-one"; + const limit$1 = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100; + const pluralSuffix = isUnique || config2.usePlural ? "" : "s"; + includes[`${model$1}${pluralSuffix}`] = isUnique ? true : { limit: limit$1 }; + if (!isUnique) pluralJoinResults.push(`${model$1}${pluralSuffix}`); + } + } + let orderBy = void 0; + if (sortBy?.field) orderBy = [sortFn(schemaModel[getFieldName({ + model, + field: sortBy?.field + })])]; + const res = await db$1.query[model].findMany({ + where: clause[0], + with: includes, + columns: select?.length && select.length > 0 ? select.reduce((acc, field) => { + acc[getFieldName({ + model, + field + })] = true; + return acc; + }, {}) : void 0, + limit: limit ?? 100, + offset: offset ?? 0, + orderBy + }); + if (res) for (const item of res) for (const pluralJoinResult of pluralJoinResults) { + const singularKey = !config2.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult; + if (singularKey === pluralJoinResult) continue; + item[singularKey] = item[pluralJoinResult]; + delete item[pluralJoinResult]; + } + return res; + } + let builder = db$1.select(select?.length && select.length > 0 ? select.reduce((acc, field) => { + const fieldName = getFieldName({ + model, + field + }); + return { + ...acc, + [fieldName]: schemaModel[fieldName] + }; + }, {}) : void 0).from(schemaModel); + const effectiveLimit = limit; + const effectiveOffset = offset; + if (typeof effectiveLimit !== "undefined") builder = builder.limit(effectiveLimit); + if (typeof effectiveOffset !== "undefined") builder = builder.offset(effectiveOffset); + if (sortBy?.field) builder = builder.orderBy(sortFn(schemaModel[getFieldName({ + model, + field: sortBy?.field + })])); + return await builder.where(...clause); + }, + async count({ model, where }) { + const schemaModel = getSchema2(model); + const clause = where ? convertWhereClause(where, model) : []; + return (await db$1.select({ count: count() }).from(schemaModel).where(...clause))[0].count; + }, + async update({ model, where, update: values }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + return await withReturning(model, db$1.update(schemaModel).set(values).where(...clause), values, where); + }, + async updateMany({ model, where, update: values }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + return await db$1.update(schemaModel).set(values).where(...clause); + }, + async delete({ model, where }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + return await db$1.delete(schemaModel).where(...clause); + }, + async deleteMany({ model, where }) { + const schemaModel = getSchema2(model); + const clause = convertWhereClause(where, model); + const res = await db$1.delete(schemaModel).where(...clause); + let count$1 = 0; + if (res && "rowCount" in res) count$1 = res.rowCount; + else if (Array.isArray(res)) count$1 = res.length; + else if (res && ("affectedRows" in res || "rowsAffected" in res || "changes" in res)) count$1 = res.affectedRows ?? res.rowsAffected ?? res.changes; + if (typeof count$1 !== "number") logger.error("[Drizzle Adapter] The result of the deleteMany operation is not a number. This is likely a bug in the adapter. Please report this issue to the Better Auth team.", { + res, + model, + where + }); + return count$1; + }, + options: config2 + }; + }; + let adapterOptions = null; + adapterOptions = { + config: { + adapterId: "drizzle", + adapterName: "Drizzle Adapter", + usePlural: config2.usePlural ?? false, + debugLogs: config2.debugLogs ?? false, + supportsUUIDs: config2.provider === "pg" ? true : false, + supportsJSON: config2.provider === "pg" ? true : false, + supportsArrays: config2.provider === "pg" ? true : false, + transaction: config2.transaction ?? false ? (cb) => db2.transaction((tx) => { + return cb(createAdapterFactory({ + config: adapterOptions.config, + adapter: createCustomAdapter(tx) + })(lazyOptions)); + }) : false + }, + adapter: createCustomAdapter(db2) + }; + const adapter = createAdapterFactory(adapterOptions); + return (options) => { + lazyOptions = options; + return adapter(options); + }; +}; +const svelteKitHandler = async ({ auth: auth2, event, resolve, building }) => { + if (building) return resolve(event); + const { request, url } = event; + if (isAuthPath(url.toString(), auth2.options)) return auth2.handler(request); + return resolve(event); +}; +function isAuthPath(url, options) { + const _url = new URL(url); + const baseURL = new URL(`${options.baseURL || _url.origin}${options.basePath || "/api/auth"}`); + if (_url.origin !== baseURL.origin) return false; + if (!_url.pathname.startsWith(baseURL.pathname.endsWith("/") ? baseURL.pathname : `${baseURL.pathname}/`)) return false; + return true; +} +const sveltekitCookies = (getRequestEvent2) => { + return { + id: "sveltekit-cookies", + hooks: { after: [{ + matcher() { + return true; + }, + handler: createAuthMiddleware(async (ctx) => { + const returned = ctx.context.responseHeaders; + if ("_flag" in ctx && ctx._flag === "router") return; + if (returned instanceof Headers) { + const setCookies = returned?.get("set-cookie"); + if (!setCookies) return; + const event = getRequestEvent2(); + if (!event) return; + const parsed = parseSetCookieHeader(setCookies); + for (const [name, { value, ...ops }] of parsed) try { + event.cookies.set(name, decodeURIComponent(value), { + sameSite: ops.samesite, + path: ops.path || "/", + expires: ops.expires, + secure: ops.secure, + httpOnly: ops.httponly, + domain: ops.domain, + maxAge: ops["max-age"] + }); + } catch { + } + } + }) + }] } + }; +}; +defineErrorCodes({ + FAILED_TO_CREATE_USER: "Failed to create user", + USER_ALREADY_EXISTS: "User already exists.", + USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL: "User already exists. Use another email.", + YOU_CANNOT_BAN_YOURSELF: "You cannot ban yourself", + YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE: "You are not allowed to change users role", + YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS: "You are not allowed to create users", + YOU_ARE_NOT_ALLOWED_TO_LIST_USERS: "You are not allowed to list users", + YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS: "You are not allowed to list users sessions", + YOU_ARE_NOT_ALLOWED_TO_BAN_USERS: "You are not allowed to ban users", + YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS: "You are not allowed to impersonate users", + YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS: "You are not allowed to revoke users sessions", + YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS: "You are not allowed to delete users", + YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD: "You are not allowed to set users password", + BANNED_USER: "You have been banned from this application", + YOU_ARE_NOT_ALLOWED_TO_GET_USER: "You are not allowed to get user", + NO_DATA_TO_UPDATE: "No data to update", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS: "You are not allowed to update users", + YOU_CANNOT_REMOVE_YOURSELF: "You cannot remove yourself", + YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE: "You are not allowed to set a non-existent role value", + YOU_CANNOT_IMPERSONATE_ADMINS: "You cannot impersonate admins", + INVALID_ROLE_TYPE: "Invalid role type" +}); +createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("UNAUTHORIZED"); + return { session }; +}); +z.object({ + userId: z.coerce.string().meta({ description: "The user id" }), + role: z.union([z.string().meta({ description: "The role to set. `admin` or `user` by default" }), z.array(z.string().meta({ description: "The roles to set. `admin` or `user` by default" }))]).meta({ description: "The role to set, this can be a string or an array of strings. Eg: `admin` or `[admin, user]`" }) +}); +z.object({ id: z.string().meta({ description: "The id of the User" }) }); +z.object({ + email: z.string().meta({ description: "The email of the user" }), + password: z.string().optional().meta({ description: "The password of the user. If not provided, the user will be created without a credential account (useful for magic link or social login only users)." }), + name: z.string().meta({ description: "The name of the user" }), + role: z.union([z.string().meta({ description: "The role of the user" }), z.array(z.string().meta({ description: "The roles of user" }))]).optional().meta({ description: `A string or array of strings representing the roles to apply to the new user. Eg: "user"` }), + data: z.record(z.string(), z.any()).optional().meta({ description: "Extra fields for the user. Including custom additional fields." }) +}); +z.object({ + userId: z.coerce.string().meta({ description: "The user id" }), + data: z.record(z.any(), z.any()).meta({ description: "The user data to update" }) +}); +z.object({ + searchValue: z.string().optional().meta({ description: 'The value to search for. Eg: "some name"' }), + searchField: z.enum(["email", "name"]).meta({ description: 'The field to search in, defaults to email. Can be `email` or `name`. Eg: "name"' }).optional(), + searchOperator: z.enum([ + "contains", + "starts_with", + "ends_with" + ]).meta({ description: 'The operator to use for the search. Can be `contains`, `starts_with` or `ends_with`. Eg: "contains"' }).optional(), + limit: z.string().meta({ description: "The number of users to return" }).or(z.number()).optional(), + offset: z.string().meta({ description: "The offset to start from" }).or(z.number()).optional(), + sortBy: z.string().meta({ description: "The field to sort by" }).optional(), + sortDirection: z.enum(["asc", "desc"]).meta({ description: "The direction to sort by" }).optional(), + filterField: z.string().meta({ description: "The field to filter by" }).optional(), + filterValue: z.string().meta({ description: "The value to filter by" }).or(z.number()).or(z.boolean()).optional(), + filterOperator: z.enum([ + "eq", + "ne", + "lt", + "lte", + "gt", + "gte", + "contains" + ]).meta({ description: "The operator to use for the filter" }).optional() +}); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ + userId: z.coerce.string().meta({ description: "The user id" }), + banReason: z.string().meta({ description: "The reason for the ban" }).optional(), + banExpiresIn: z.number().meta({ description: "The number of seconds until the ban expires" }).optional() +}); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ sessionToken: z.string().meta({ description: "The session token" }) }); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ userId: z.coerce.string().meta({ description: "The user id" }) }); +z.object({ + newPassword: z.string().nonempty("newPassword cannot be empty").meta({ description: "The new password" }), + userId: z.coerce.string().nonempty("userId cannot be empty").meta({ description: "The user id" }) +}); +z.object({ + userId: z.coerce.string().optional().meta({ description: `The user id. Eg: "user-id"` }), + role: z.string().optional().meta({ description: `The role to check permission for. Eg: "admin"` }) +}).and(z.union([z.object({ + permission: z.record(z.string(), z.array(z.string())), + permissions: z.undefined() +}), z.object({ + permission: z.undefined(), + permissions: z.record(z.string(), z.array(z.string())) +})])); +defineErrorCodes({ + INVALID_EMAIL_FORMAT: "Email was not generated in a valid format", + FAILED_TO_CREATE_USER: "Failed to create user", + COULD_NOT_CREATE_SESSION: "Could not create session", + ANONYMOUS_USERS_CANNOT_SIGN_IN_AGAIN_ANONYMOUSLY: "Anonymous users cannot sign in again anonymously", + FAILED_TO_DELETE_ANONYMOUS_USER: "Failed to delete anonymous user", + USER_IS_NOT_ANONYMOUS: "User is not anonymous", + DELETE_ANONYMOUS_USER_DISABLED: "Deleting anonymous users is disabled" +}); +z.object({ + key: z.string().meta({ description: "The key to verify" }), + permissions: z.record(z.string(), z.array(z.string())).meta({ description: "The permissions to verify." }).optional() +}); +z.object({ + name: z.string().meta({ description: "Name of the Api Key" }).optional(), + expiresIn: z.number().meta({ description: "Expiration time of the Api Key in seconds" }).min(1).optional().nullable().default(null), + userId: z.coerce.string().meta({ description: 'User Id of the user that the Api Key belongs to. server-only. Eg: "user-id"' }).optional(), + prefix: z.string().meta({ description: "Prefix of the Api Key" }).regex(/^[a-zA-Z0-9_-]+$/, { message: "Invalid prefix format, must be alphanumeric and contain only underscores and hyphens." }).optional(), + remaining: z.number().meta({ description: "Remaining number of requests. Server side only" }).min(0).optional().nullable().default(null), + metadata: z.any().optional(), + refillAmount: z.number().meta({ description: "Amount to refill the remaining count of the Api Key. server-only. Eg: 100" }).min(1).optional(), + refillInterval: z.number().meta({ description: "Interval to refill the Api Key in milliseconds. server-only. Eg: 1000" }).optional(), + rateLimitTimeWindow: z.number().meta({ description: "The duration in milliseconds where each request is counted. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 1000" }).optional(), + rateLimitMax: z.number().meta({ description: "Maximum amount of requests allowed within a window. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 100" }).optional(), + rateLimitEnabled: z.boolean().meta({ description: "Whether the key has rate limiting enabled. server-only. Eg: true" }).optional(), + permissions: z.record(z.string(), z.array(z.string())).meta({ description: "Permissions of the Api Key." }).optional() +}); +z.object({ keyId: z.string().meta({ description: "The id of the Api Key" }) }); +z.object({ id: z.string().meta({ description: "The id of the Api Key" }) }); +z.object({ + keyId: z.string().meta({ description: "The id of the Api Key" }), + userId: z.coerce.string().meta({ description: 'The id of the user which the api key belongs to. server-only. Eg: "some-user-id"' }).optional(), + name: z.string().meta({ description: "The name of the key" }).optional(), + enabled: z.boolean().meta({ description: "Whether the Api Key is enabled or not" }).optional(), + remaining: z.number().meta({ description: "The number of remaining requests" }).min(1).optional(), + refillAmount: z.number().meta({ description: "The refill amount" }).optional(), + refillInterval: z.number().meta({ description: "The refill interval" }).optional(), + metadata: z.any().optional(), + expiresIn: z.number().meta({ description: "Expiration time of the Api Key in seconds" }).min(1).optional().nullable(), + rateLimitEnabled: z.boolean().meta({ description: "Whether the key has rate limiting enabled." }).optional(), + rateLimitTimeWindow: z.number().meta({ description: "The duration in milliseconds where each request is counted. server-only. Eg: 1000" }).optional(), + rateLimitMax: z.number().meta({ description: "Maximum amount of requests allowed within a window. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 100" }).optional(), + permissions: z.record(z.string(), z.array(z.string())).meta({ description: "Update the permissions on the API Key. server-only." }).optional().nullable() +}); +defineErrorCodes({ + INVALID_METADATA_TYPE: "metadata must be an object or undefined", + REFILL_AMOUNT_AND_INTERVAL_REQUIRED: "refillAmount is required when refillInterval is provided", + REFILL_INTERVAL_AND_AMOUNT_REQUIRED: "refillInterval is required when refillAmount is provided", + USER_BANNED: "User is banned", + UNAUTHORIZED_SESSION: "Unauthorized or invalid session", + KEY_NOT_FOUND: "API Key not found", + KEY_DISABLED: "API Key is disabled", + KEY_EXPIRED: "API Key has expired", + USAGE_EXCEEDED: "API Key has reached its usage limit", + KEY_NOT_RECOVERABLE: "API Key is not recoverable", + EXPIRES_IN_IS_TOO_SMALL: "The expiresIn is smaller than the predefined minimum value.", + EXPIRES_IN_IS_TOO_LARGE: "The expiresIn is larger than the predefined maximum value.", + INVALID_REMAINING: "The remaining count is either too large or too small.", + INVALID_PREFIX_LENGTH: "The prefix length is either too large or too small.", + INVALID_NAME_LENGTH: "The name length is either too large or too small.", + METADATA_DISABLED: "Metadata is disabled.", + RATE_LIMIT_EXCEEDED: "Rate limit exceeded.", + NO_VALUES_TO_UPDATE: "No values to update.", + KEY_DISABLED_EXPIRATION: "Custom key expiration values are disabled.", + INVALID_API_KEY: "Invalid API key.", + INVALID_USER_ID_FROM_API_KEY: "The user id from the API key is invalid.", + INVALID_API_KEY_GETTER_RETURN_TYPE: "API Key getter returned an invalid key type. Expected string.", + SERVER_ONLY_PROPERTY: "The property you're trying to set can only be set from the server auth instance only.", + FAILED_TO_UPDATE_API_KEY: "Failed to update API key", + NAME_REQUIRED: "API Key name is required." +}); +defineErrorCodes({ + VERIFICATION_FAILED: "Captcha verification failed", + MISSING_RESPONSE: "Missing CAPTCHA response", + UNKNOWN_ERROR: "Something went wrong" +}); +defineErrorCodes({ + MISSING_SECRET_KEY: "Missing secret key", + SERVICE_UNAVAILABLE: "CAPTCHA service unavailable" +}); +z.optional(z.object({ + disableCookieCache: z.boolean().meta({ description: "Disable cookie cache and fetch session from database" }).or(z.string().transform((v) => v === "true")).optional(), + disableRefresh: z.boolean().meta({ description: "Disable session refresh. Useful for checking session status, without updating the session" }).optional() +})); +const DEVICE_AUTHORIZATION_ERROR_CODES = defineErrorCodes({ + INVALID_DEVICE_CODE: "Invalid device code", + EXPIRED_DEVICE_CODE: "Device code has expired", + EXPIRED_USER_CODE: "User code has expired", + AUTHORIZATION_PENDING: "Authorization pending", + ACCESS_DENIED: "Access denied", + INVALID_USER_CODE: "Invalid user code", + DEVICE_CODE_ALREADY_PROCESSED: "Device code already processed", + POLLING_TOO_FREQUENTLY: "Polling too frequently", + USER_NOT_FOUND: "User not found", + FAILED_TO_CREATE_SESSION: "Failed to create session", + INVALID_DEVICE_CODE_STATUS: "Invalid device code status", + AUTHENTICATION_REQUIRED: "Authentication required" +}); +z.object({ + client_id: z.string().meta({ description: "The client ID of the application" }), + scope: z.string().meta({ description: "Space-separated list of scopes" }).optional() +}); +z.object({ + error: z.enum(["invalid_request", "invalid_client"]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) +}); +z.object({ + grant_type: z.literal("urn:ietf:params:oauth:grant-type:device_code").meta({ description: "The grant type for device flow" }), + device_code: z.string().meta({ description: "The device verification code" }), + client_id: z.string().meta({ description: "The client ID of the application" }) +}); +z.object({ + error: z.enum([ + "authorization_pending", + "slow_down", + "expired_token", + "access_denied", + "invalid_request", + "invalid_grant" + ]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) +}); +createAuthEndpoint("/device", { + method: "GET", + query: z.object({ user_code: z.string().meta({ description: "The user code to verify" }) }), + error: z.object({ + error: z.enum(["invalid_request"]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) + }), + metadata: { openapi: { + description: "Verify user code and get device authorization status", + responses: { 200: { + description: "Device authorization status", + content: { "application/json": { schema: { + type: "object", + properties: { + user_code: { + type: "string", + description: "The user code to verify" + }, + status: { + type: "string", + enum: [ + "pending", + "approved", + "denied" + ], + description: "Current status of the device authorization" + } + } + } } } + } } + } } +}, async (ctx) => { + const { user_code } = ctx.query; + const cleanUserCode = user_code.replace(/-/g, ""); + const deviceCodeRecord = await ctx.context.adapter.findOne({ + model: "deviceCode", + where: [{ + field: "userCode", + value: cleanUserCode + }] + }); + if (!deviceCodeRecord) throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE + }); + if (deviceCodeRecord.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { + error: "expired_token", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE + }); + return ctx.json({ + user_code, + status: deviceCodeRecord.status + }); +}); +createAuthEndpoint("/device/approve", { + method: "POST", + body: z.object({ userCode: z.string().meta({ description: "The user code to approve" }) }), + error: z.object({ + error: z.enum([ + "invalid_request", + "expired_token", + "device_code_already_processed", + "unauthorized", + "access_denied" + ]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) + }), + requireHeaders: true, + metadata: { openapi: { + description: "Approve device authorization", + responses: { 200: { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { success: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("UNAUTHORIZED", { + error: "unauthorized", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.AUTHENTICATION_REQUIRED + }); + const { userCode } = ctx.body; + const cleanUserCode = userCode.replace(/-/g, ""); + const deviceCodeRecord = await ctx.context.adapter.findOne({ + model: "deviceCode", + where: [{ + field: "userCode", + value: cleanUserCode + }] + }); + if (!deviceCodeRecord) throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE + }); + if (deviceCodeRecord.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { + error: "expired_token", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE + }); + if (deviceCodeRecord.status !== "pending") throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.DEVICE_CODE_ALREADY_PROCESSED + }); + if (deviceCodeRecord.userId && deviceCodeRecord.userId !== session.user.id) throw new APIError("FORBIDDEN", { + error: "access_denied", + error_description: "You are not authorized to approve this device authorization" + }); + await ctx.context.adapter.update({ + model: "deviceCode", + where: [{ + field: "id", + value: deviceCodeRecord.id + }], + update: { + status: "approved", + userId: session.user.id + } + }); + return ctx.json({ success: true }); +}); +createAuthEndpoint("/device/deny", { + method: "POST", + body: z.object({ userCode: z.string().meta({ description: "The user code to deny" }) }), + error: z.object({ + error: z.enum([ + "invalid_request", + "expired_token", + "unauthorized", + "access_denied" + ]).meta({ description: "Error code" }), + error_description: z.string().meta({ description: "Detailed error description" }) + }), + requireHeaders: true, + metadata: { openapi: { + description: "Deny device authorization", + responses: { 200: { + description: "Success", + content: { "application/json": { schema: { + type: "object", + properties: { success: { type: "boolean" } } + } } } + } } + } } +}, async (ctx) => { + const session = await getSessionFromCtx(ctx); + if (!session) throw new APIError("UNAUTHORIZED", { + error: "unauthorized", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.AUTHENTICATION_REQUIRED + }); + const { userCode } = ctx.body; + const cleanUserCode = userCode.replace(/-/g, ""); + const deviceCodeRecord = await ctx.context.adapter.findOne({ + model: "deviceCode", + where: [{ + field: "userCode", + value: cleanUserCode + }] + }); + if (!deviceCodeRecord) throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE + }); + if (deviceCodeRecord.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { + error: "expired_token", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE + }); + if (deviceCodeRecord.status !== "pending") throw new APIError("BAD_REQUEST", { + error: "invalid_request", + error_description: DEVICE_AUTHORIZATION_ERROR_CODES.DEVICE_CODE_ALREADY_PROCESSED + }); + if (deviceCodeRecord.userId && deviceCodeRecord.userId !== session.user.id) throw new APIError("FORBIDDEN", { + error: "access_denied", + error_description: "You are not authorized to deny this device authorization" + }); + await ctx.context.adapter.update({ + model: "deviceCode", + where: [{ + field: "id", + value: deviceCodeRecord.id + }], + update: { + status: "denied", + userId: deviceCodeRecord.userId || session.user.id + } + }); + return ctx.json({ success: true }); +}); +z.object({ + id: z.string(), + deviceCode: z.string(), + userCode: z.string(), + userId: z.string().optional(), + expiresAt: z.date(), + status: z.string(), + lastPolledAt: z.date().optional(), + pollingInterval: z.number().optional(), + clientId: z.string().optional(), + scope: z.string().optional() +}); +const timeStringSchema = z.custom((val) => { + if (typeof val !== "string") return false; + try { + ms(val); + return true; + } catch { + return false; + } +}, { message: "Invalid time string format. Use formats like '30m', '5s', '1h', etc." }); +z.object({ + expiresIn: timeStringSchema.default("30m").describe("Time in seconds until the device code expires. Use formats like '30m', '5s', '1h', etc."), + interval: timeStringSchema.default("5s").describe("Time in seconds between polling attempts. Use formats like '30m', '5s', '1h', etc."), + deviceCodeLength: z.number().int().positive().default(40).describe("Length of the device code to be generated. Default is 40 characters."), + userCodeLength: z.number().int().positive().default(8).describe("Length of the user code to be generated. Default is 8 characters."), + generateDeviceCode: z.custom((val) => typeof val === "function", { message: "generateDeviceCode must be a function that returns a string or a promise that resolves to a string." }).optional().describe("Function to generate a device code. If not provided, a default random string generator will be used."), + generateUserCode: z.custom((val) => typeof val === "function", { message: "generateUserCode must be a function that returns a string or a promise that resolves to a string." }).optional().describe("Function to generate a user code. If not provided, a default random string generator will be used."), + validateClient: z.custom((val) => typeof val === "function", { message: "validateClient must be a function that returns a boolean or a promise that resolves to a boolean." }).optional().describe("Function to validate the client ID. If not provided, no validation will be performed."), + onDeviceAuthRequest: z.custom((val) => typeof val === "function", { message: "onDeviceAuthRequest must be a function that returns void or a promise that resolves to void." }).optional().describe("Function to handle device authorization requests. If not provided, no additional actions will be taken."), + verificationUri: z.string().optional().describe("The URI where users verify their device code. Can be an absolute URL (https://example.com/device) or relative path (/custom-path). This will be returned as verification_uri in the device code response. If not provided, defaults to /device."), + schema: z.custom(() => true) +}); +const types = [ + "email-verification", + "sign-in", + "forget-password" +]; +defineErrorCodes({ + OTP_EXPIRED: "OTP expired", + INVALID_OTP: "Invalid OTP", + TOO_MANY_ATTEMPTS: "Too many attempts" +}); +z.object({ + email: z.string({}).meta({ description: "Email address to send the OTP" }), + type: z.enum(types).meta({ description: "Type of the OTP" }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address to send the OTP" }), + type: z.enum(types).meta({ + required: true, + description: "Type of the OTP" + }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address the OTP was sent to" }), + type: z.enum(types).meta({ + required: true, + description: "Type of the OTP" + }) +}); +z.object({ + email: z.string().meta({ description: "Email address the OTP was sent to" }), + type: z.enum(types).meta({ + required: true, + description: "Type of the OTP" + }), + otp: z.string().meta({ + required: true, + description: "OTP to verify" + }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address to verify" }), + otp: z.string().meta({ + required: true, + description: "OTP to verify" + }) +}); +z.object({ + email: z.string({}).meta({ description: "Email address to sign in" }), + otp: z.string().meta({ + required: true, + description: "OTP sent to the email" + }) +}); +z.object({ email: z.string().meta({ description: "Email address to send the OTP" }) }); +z.object({ email: z.string().meta({ description: "Email address to send the OTP" }) }); +z.object({ + email: z.string().meta({ description: "Email address to reset the password" }), + otp: z.string().meta({ description: "OTP sent to the email" }), + password: z.string().meta({ description: "New password" }) +}); +const GENERIC_OAUTH_ERROR_CODES = defineErrorCodes({ + INVALID_OAUTH_CONFIGURATION: "Invalid OAuth configuration", + TOKEN_URL_NOT_FOUND: "Invalid OAuth configuration. Token URL not found.", + PROVIDER_CONFIG_NOT_FOUND: "No config found for provider", + PROVIDER_ID_REQUIRED: "Provider ID is required", + INVALID_OAUTH_CONFIG: "Invalid OAuth configuration.", + SESSION_REQUIRED: "Session is required", + ISSUER_MISMATCH: "OAuth issuer mismatch. The authorization server issuer does not match the expected value (RFC 9207).", + ISSUER_MISSING: "OAuth issuer parameter missing. The authorization server did not include the required iss parameter (RFC 9207)." +}); +const signInWithOAuth2BodySchema = z.object({ + providerId: z.string().meta({ description: "The provider ID for the OAuth provider" }), + callbackURL: z.string().meta({ description: "The URL to redirect to after sign in" }).optional(), + errorCallbackURL: z.string().meta({ description: "The URL to redirect to if an error occurs" }).optional(), + newUserCallbackURL: z.string().meta({ description: 'The URL to redirect to after login if the user is new. Eg: "/welcome"' }).optional(), + disableRedirect: z.boolean().meta({ description: "Disable redirect" }).optional(), + scopes: z.array(z.string()).meta({ description: "Scopes to be passed to the provider authorization request." }).optional(), + requestSignUp: z.boolean().meta({ description: "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider. Eg: false" }).optional(), + additionalData: z.record(z.string(), z.any()).optional() +}); +const signInWithOAuth2 = (options) => createAuthEndpoint("/sign-in/oauth2", { + method: "POST", + body: signInWithOAuth2BodySchema, + metadata: { openapi: { + description: "Sign in with OAuth2", + responses: { 200: { + description: "Sign in with OAuth2", + content: { "application/json": { schema: { + type: "object", + properties: { + url: { type: "string" }, + redirect: { type: "boolean" } + } + } } } + } } + } } +}, async (ctx) => { + const { providerId } = ctx.body; + const config2 = options.config.find((c) => c.providerId === providerId); + if (!config2) throw new APIError("BAD_REQUEST", { message: `${GENERIC_OAUTH_ERROR_CODES.PROVIDER_CONFIG_NOT_FOUND} ${providerId}` }); + const { discoveryUrl, authorizationUrl, tokenUrl, clientId, clientSecret, scopes, redirectURI, responseType, pkce, prompt, accessType, authorizationUrlParams, responseMode } = config2; + let finalAuthUrl = authorizationUrl; + let finalTokenUrl = tokenUrl; + if (discoveryUrl) { + const discovery = await betterFetch(discoveryUrl, { + method: "GET", + headers: config2.discoveryHeaders, + onError(context) { + ctx.context.logger.error(context.error.message, context.error, { discoveryUrl }); + } + }); + if (discovery.data) { + finalAuthUrl = discovery.data.authorization_endpoint; + finalTokenUrl = discovery.data.token_endpoint; + } + } + if (!finalAuthUrl || !finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + if (authorizationUrlParams) { + const withAdditionalParams = new URL(finalAuthUrl); + for (const [paramName, paramValue] of Object.entries(authorizationUrlParams)) withAdditionalParams.searchParams.set(paramName, paramValue); + finalAuthUrl = withAdditionalParams.toString(); + } + const additionalParams = typeof authorizationUrlParams === "function" ? authorizationUrlParams(ctx) : authorizationUrlParams; + const { state, codeVerifier } = await generateState(ctx, void 0, ctx.body.additionalData); + const authUrl = await createAuthorizationURL({ + id: providerId, + options: { + clientId, + clientSecret, + redirectURI + }, + authorizationEndpoint: finalAuthUrl, + state, + codeVerifier: pkce ? codeVerifier : void 0, + scopes: ctx.body.scopes ? [...ctx.body.scopes, ...scopes || []] : scopes || [], + redirectURI: `${ctx.context.baseURL}/oauth2/callback/${providerId}`, + prompt, + accessType, + responseType, + responseMode, + additionalParams + }); + return ctx.json({ + url: authUrl.toString(), + redirect: !ctx.body.disableRedirect + }); +}); +const OAuth2CallbackQuerySchema = z.object({ + code: z.string().meta({ description: "The OAuth2 code" }).optional(), + error: z.string().meta({ description: "The error message, if any" }).optional(), + error_description: z.string().meta({ description: "The error description, if any" }).optional(), + state: z.string().meta({ description: "The state parameter from the OAuth2 request" }).optional(), + iss: z.string().meta({ description: "The issuer identifier" }).optional() +}); +const oAuth2Callback = (options) => createAuthEndpoint("/oauth2/callback/:providerId", { + method: "GET", + query: OAuth2CallbackQuerySchema, + metadata: { + ...HIDE_METADATA, + allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"], + openapi: { + description: "OAuth2 callback", + responses: { 200: { + description: "OAuth2 callback", + content: { "application/json": { schema: { + type: "object", + properties: { url: { type: "string" } } + } } } + } } + } + } +}, async (ctx) => { + const defaultErrorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`; + if (ctx.query.error || !ctx.query.code) throw ctx.redirect(`${defaultErrorURL}?error=${ctx.query.error || "oAuth_code_missing"}&error_description=${ctx.query.error_description}`); + const providerId = ctx.params?.providerId; + if (!providerId) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.PROVIDER_ID_REQUIRED }); + const providerConfig = options.config.find((p) => p.providerId === providerId); + if (!providerConfig) throw new APIError("BAD_REQUEST", { message: `${GENERIC_OAUTH_ERROR_CODES.PROVIDER_CONFIG_NOT_FOUND} ${providerId}` }); + let tokens = void 0; + const { callbackURL, codeVerifier, errorURL, requestSignUp, newUserURL, link } = await parseState(ctx); + const code = ctx.query.code; + function redirectOnError(error2) { + const defaultErrorURL$1 = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`; + let url = errorURL || defaultErrorURL$1; + if (url.includes("?")) url = `${url}&error=${error2}`; + else url = `${url}?error=${error2}`; + throw ctx.redirect(url); + } + let finalTokenUrl = providerConfig.tokenUrl; + let finalUserInfoUrl = providerConfig.userInfoUrl; + let expectedIssuer = providerConfig.issuer; + if (providerConfig.discoveryUrl) { + const discovery = await betterFetch(providerConfig.discoveryUrl, { + method: "GET", + headers: providerConfig.discoveryHeaders + }); + if (discovery.data) { + finalTokenUrl = discovery.data.token_endpoint; + finalUserInfoUrl = discovery.data.userinfo_endpoint; + if (!expectedIssuer && discovery.data.issuer) expectedIssuer = discovery.data.issuer; + } + } + if (expectedIssuer) { + if (ctx.query.iss) { + if (ctx.query.iss !== expectedIssuer) { + ctx.context.logger.error("OAuth issuer mismatch", { + expected: expectedIssuer, + received: ctx.query.iss + }); + return redirectOnError("issuer_mismatch"); + } + } else if (providerConfig.requireIssuerValidation) { + ctx.context.logger.error("OAuth issuer parameter missing", { expected: expectedIssuer }); + return redirectOnError("issuer_missing"); + } + } + try { + if (providerConfig.getToken) tokens = await providerConfig.getToken({ + code, + redirectURI: `${ctx.context.baseURL}/oauth2/callback/${providerConfig.providerId}`, + codeVerifier: providerConfig.pkce ? codeVerifier : void 0 + }); + else { + if (!finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIG }); + const additionalParams = typeof providerConfig.tokenUrlParams === "function" ? providerConfig.tokenUrlParams(ctx) : providerConfig.tokenUrlParams; + tokens = await validateAuthorizationCode({ + headers: providerConfig.authorizationHeaders, + code, + codeVerifier: providerConfig.pkce ? codeVerifier : void 0, + redirectURI: `${ctx.context.baseURL}/oauth2/callback/${providerConfig.providerId}`, + options: { + clientId: providerConfig.clientId, + clientSecret: providerConfig.clientSecret, + redirectURI: providerConfig.redirectURI + }, + tokenEndpoint: finalTokenUrl, + authentication: providerConfig.authentication, + additionalParams + }); + } + } catch (e) { + ctx.context.logger.error(e && typeof e === "object" && "name" in e ? e.name : "", e); + throw redirectOnError("oauth_code_verification_failed"); + } + if (!tokens) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIG }); + const userInfo = await (async function handleUserInfo() { + const userInfo$1 = providerConfig.getUserInfo ? await providerConfig.getUserInfo(tokens) : await getUserInfo(tokens, finalUserInfoUrl); + if (!userInfo$1) throw redirectOnError("user_info_is_missing"); + const mapUser = providerConfig.mapProfileToUser ? await providerConfig.mapProfileToUser(userInfo$1) : userInfo$1; + const email = mapUser.email ? mapUser.email.toLowerCase() : userInfo$1.email?.toLowerCase(); + if (!email) { + ctx.context.logger.error("Unable to get user info", userInfo$1); + throw redirectOnError("email_is_missing"); + } + const id = mapUser.id ? String(mapUser.id) : String(userInfo$1.id); + const name = mapUser.name ? mapUser.name : userInfo$1.name; + if (!name) { + ctx.context.logger.error("Unable to get user info", userInfo$1); + throw redirectOnError("name_is_missing"); + } + return { + ...userInfo$1, + ...mapUser, + email, + id, + name + }; + })(); + if (link) { + if (ctx.context.options.account?.accountLinking?.allowDifferentEmails !== true && link.email.toLowerCase() !== userInfo.email.toLowerCase()) return redirectOnError("email_doesn't_match"); + const existingAccount = await ctx.context.internalAdapter.findAccountByProviderId(String(userInfo.id), providerConfig.providerId); + if (existingAccount) { + if (existingAccount.userId !== link.userId) return redirectOnError("account_already_linked_to_different_user"); + const updateData = Object.fromEntries(Object.entries({ + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + idToken: tokens.idToken, + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(",") + }).filter(([_, value]) => value !== void 0)); + await ctx.context.internalAdapter.updateAccount(existingAccount.id, updateData); + } else if (!await ctx.context.internalAdapter.createAccount({ + userId: link.userId, + providerId: providerConfig.providerId, + accountId: userInfo.id, + accessToken: await setTokenUtil(tokens.accessToken, ctx.context), + accessTokenExpiresAt: tokens.accessTokenExpiresAt, + refreshTokenExpiresAt: tokens.refreshTokenExpiresAt, + scope: tokens.scopes?.join(","), + refreshToken: await setTokenUtil(tokens.refreshToken, ctx.context), + idToken: tokens.idToken + })) return redirectOnError("unable_to_link_account"); + let toRedirectTo$1; + try { + toRedirectTo$1 = callbackURL.toString(); + } catch { + toRedirectTo$1 = callbackURL; + } + throw ctx.redirect(toRedirectTo$1); + } + const result = await handleOAuthUserInfo(ctx, { + userInfo, + account: { + providerId: providerConfig.providerId, + accountId: userInfo.id, + ...tokens, + scope: tokens.scopes?.join(",") + }, + callbackURL, + disableSignUp: providerConfig.disableImplicitSignUp && !requestSignUp || providerConfig.disableSignUp, + overrideUserInfo: providerConfig.overrideUserInfo + }); + if (result.error) return redirectOnError(result.error.split(" ").join("_")); + const { session, user } = result.data; + await setSessionCookie(ctx, { + session, + user + }); + let toRedirectTo; + try { + toRedirectTo = (result.isRegister ? newUserURL || callbackURL : callbackURL).toString(); + } catch { + toRedirectTo = result.isRegister ? newUserURL || callbackURL : callbackURL; + } + throw ctx.redirect(toRedirectTo); +}); +const OAuth2LinkAccountBodySchema = z.object({ + providerId: z.string(), + callbackURL: z.string(), + scopes: z.array(z.string()).meta({ description: "Additional scopes to request when linking the account" }).optional(), + errorCallbackURL: z.string().meta({ description: "The URL to redirect to if there is an error during the link process" }).optional() +}); +const oAuth2LinkAccount = (options) => createAuthEndpoint("/oauth2/link", { + method: "POST", + body: OAuth2LinkAccountBodySchema, + use: [sessionMiddleware], + metadata: { openapi: { + description: "Link an OAuth2 account to the current user session", + responses: { "200": { + description: "Authorization URL generated successfully for linking an OAuth2 account", + content: { "application/json": { schema: { + type: "object", + properties: { + url: { + type: "string", + format: "uri", + description: "The authorization URL to redirect the user to for linking the OAuth2 account" + }, + redirect: { + type: "boolean", + description: "Indicates that the client should redirect to the provided URL", + enum: [true] + } + }, + required: ["url", "redirect"] + } } } + } } + } } +}, async (c) => { + const session = c.context.session; + if (!session) throw new APIError("UNAUTHORIZED", { message: GENERIC_OAUTH_ERROR_CODES.SESSION_REQUIRED }); + const provider = options.config.find((p) => p.providerId === c.body.providerId); + if (!provider) throw new APIError("NOT_FOUND", { message: BASE_ERROR_CODES.PROVIDER_NOT_FOUND }); + const { providerId, clientId, clientSecret, redirectURI, authorizationUrl, discoveryUrl, pkce, scopes, prompt, accessType, authorizationUrlParams } = provider; + let finalAuthUrl = authorizationUrl; + if (!finalAuthUrl) { + if (!discoveryUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + const discovery = await betterFetch(discoveryUrl, { + method: "GET", + headers: provider.discoveryHeaders, + onError(context) { + c.context.logger.error(context.error.message, context.error, { discoveryUrl }); + } + }); + if (discovery.data) finalAuthUrl = discovery.data.authorization_endpoint; + } + if (!finalAuthUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + const state = await generateState(c, { + userId: session.user.id, + email: session.user.email + }, void 0); + const additionalParams = typeof authorizationUrlParams === "function" ? authorizationUrlParams(c) : authorizationUrlParams; + const url = await createAuthorizationURL({ + id: providerId, + options: { + clientId, + clientSecret, + redirectURI: redirectURI || `${c.context.baseURL}/oauth2/callback/${providerId}` + }, + authorizationEndpoint: finalAuthUrl, + state: state.state, + codeVerifier: pkce ? state.codeVerifier : void 0, + scopes: c.body.scopes || scopes || [], + redirectURI: redirectURI || `${c.context.baseURL}/oauth2/callback/${providerId}`, + prompt, + accessType, + additionalParams + }); + return c.json({ + url: url.toString(), + redirect: true + }); +}); +async function getUserInfo(tokens, finalUserInfoUrl) { + if (tokens.idToken) { + const decoded = decodeJwt(tokens.idToken); + if (decoded) { + if (decoded.sub && decoded.email) return { + id: decoded.sub, + emailVerified: decoded.email_verified, + image: decoded.picture, + ...decoded + }; + } + } + if (!finalUserInfoUrl) return null; + const userInfo = await betterFetch(finalUserInfoUrl, { + method: "GET", + headers: { Authorization: `Bearer ${tokens.accessToken}` } + }); + return { + id: userInfo.data?.sub ?? "", + emailVerified: userInfo.data?.email_verified ?? false, + email: userInfo.data?.email, + image: userInfo.data?.picture, + name: userInfo.data?.name, + ...userInfo.data + }; +} +const genericOAuth = (options) => { + const seenIds = /* @__PURE__ */ new Set(); + const nonUniqueIds = /* @__PURE__ */ new Set(); + for (const config2 of options.config) { + const id = config2.providerId; + if (seenIds.has(id)) nonUniqueIds.add(id); + seenIds.add(id); + } + if (nonUniqueIds.size > 0) console.warn(`Duplicate provider IDs found: ${Array.from(nonUniqueIds).join(", ")}`); + return { + id: "generic-oauth", + init: (ctx) => { + return { context: { socialProviders: options.config.map((c) => { + let finalUserInfoUrl = c.userInfoUrl; + return { + id: c.providerId, + name: c.providerId, + async createAuthorizationURL(data) { + let finalAuthUrl = c.authorizationUrl; + if (!finalAuthUrl && c.discoveryUrl) { + const discovery = await betterFetch(c.discoveryUrl, { + method: "GET", + headers: c.discoveryHeaders + }); + if (discovery.data) { + finalAuthUrl = discovery.data.authorization_endpoint; + finalUserInfoUrl = finalUserInfoUrl ?? discovery.data.userinfo_endpoint; + } + } + if (!finalAuthUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION }); + return createAuthorizationURL({ + id: c.providerId, + options: { + clientId: c.clientId, + clientSecret: c.clientSecret, + redirectURI: c.redirectURI + }, + authorizationEndpoint: finalAuthUrl, + state: data.state, + codeVerifier: c.pkce ? data.codeVerifier : void 0, + scopes: c.scopes || [], + redirectURI: `${ctx.baseURL}/oauth2/callback/${c.providerId}` + }); + }, + async validateAuthorizationCode(data) { + if (c.getToken) return c.getToken(data); + let finalTokenUrl = c.tokenUrl; + if (c.discoveryUrl) { + const discovery = await betterFetch(c.discoveryUrl, { + method: "GET", + headers: c.discoveryHeaders + }); + if (discovery.data) { + finalTokenUrl = discovery.data.token_endpoint; + finalUserInfoUrl = discovery.data.userinfo_endpoint; + } + } + if (!finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.TOKEN_URL_NOT_FOUND }); + return validateAuthorizationCode({ + headers: c.authorizationHeaders, + code: data.code, + codeVerifier: data.codeVerifier, + redirectURI: data.redirectURI, + options: { + clientId: c.clientId, + clientSecret: c.clientSecret, + redirectURI: c.redirectURI + }, + tokenEndpoint: finalTokenUrl, + authentication: c.authentication + }); + }, + async refreshAccessToken(refreshToken2) { + let finalTokenUrl = c.tokenUrl; + if (c.discoveryUrl) { + const discovery = await betterFetch(c.discoveryUrl, { + method: "GET", + headers: c.discoveryHeaders + }); + if (discovery.data) finalTokenUrl = discovery.data.token_endpoint; + } + if (!finalTokenUrl) throw new APIError("BAD_REQUEST", { message: GENERIC_OAUTH_ERROR_CODES.TOKEN_URL_NOT_FOUND }); + return refreshAccessToken({ + refreshToken: refreshToken2, + options: { + clientId: c.clientId, + clientSecret: c.clientSecret + }, + authentication: c.authentication, + tokenEndpoint: finalTokenUrl + }); + }, + async getUserInfo(tokens) { + const userInfo = c.getUserInfo ? await c.getUserInfo(tokens) : await getUserInfo(tokens, finalUserInfoUrl); + if (!userInfo) return null; + const userMap = await c.mapProfileToUser?.(userInfo); + return { + user: { + id: userInfo?.id, + email: userInfo?.email, + emailVerified: userInfo?.emailVerified, + image: userInfo?.image, + name: userInfo?.name, + ...userMap + }, + data: userInfo + }; + }, + options: { overrideUserInfoOnSignIn: c.overrideUserInfo } + }; + }).concat(ctx.socialProviders) } }; + }, + endpoints: { + signInWithOAuth2: signInWithOAuth2(options), + oAuth2Callback: oAuth2Callback(options), + oAuth2LinkAccount: oAuth2LinkAccount(options) + }, + options, + $ERROR_CODES: GENERIC_OAUTH_ERROR_CODES + }; +}; +defineErrorCodes({ PASSWORD_COMPROMISED: "The password you entered has been compromised. Please choose a different password." }); +z.object({ + payload: z.record(z.string(), z.any()), + overrideOptions: z.record(z.string(), z.any()).optional() +}); +z.object({ + token: z.string(), + issuer: z.string().optional() +}); +z.object({ + email: z.email().meta({ description: "Email address to send the magic link" }), + name: z.string().meta({ description: 'User display name. Only used if the user is registering for the first time. Eg: "my-name"' }).optional(), + callbackURL: z.string().meta({ description: "URL to redirect after magic link verification" }).optional(), + newUserCallbackURL: z.string().meta({ description: "URL to redirect after new user signup. Only used if the user is registering for the first time." }).optional(), + errorCallbackURL: z.string().meta({ description: "URL to redirect after error." }).optional() +}); +z.object({ + token: z.string().meta({ description: "Verification token" }), + callbackURL: z.string().meta({ description: 'URL to redirect after magic link verification, if not provided the user will be redirected to the root URL. Eg: "/dashboard"' }).optional(), + errorCallbackURL: z.string().meta({ description: "URL to redirect after error." }).optional(), + newUserCallbackURL: z.string().meta({ description: "URL to redirect after new user signup. Only used if the user is registering for the first time." }).optional() +}); +z.object({ + clientId: z.string(), + clientSecret: z.string().optional(), + type: z.enum([ + "web", + "native", + "user-agent-based", + "public" + ]), + name: z.string(), + icon: z.string().optional(), + metadata: z.string().optional(), + disabled: z.boolean().optional().default(false), + redirectUrls: z.string(), + userId: z.string().optional(), + createdAt: z.date(), + updatedAt: z.date() +}); +z.object({ + accept: z.boolean(), + consent_code: z.string().optional().nullish() +}); +z.record(z.any(), z.any()); +z.object({ + redirect_uris: z.array(z.string()).meta({ description: 'A list of redirect URIs. Eg: ["https://client.example.com/callback"]' }), + token_endpoint_auth_method: z.enum([ + "none", + "client_secret_basic", + "client_secret_post" + ]).meta({ description: 'The authentication method for the token endpoint. Eg: "client_secret_basic"' }).default("client_secret_basic").optional(), + grant_types: z.array(z.enum([ + "authorization_code", + "implicit", + "password", + "client_credentials", + "refresh_token", + "urn:ietf:params:oauth:grant-type:jwt-bearer", + "urn:ietf:params:oauth:grant-type:saml2-bearer" + ])).meta({ description: 'The grant types supported by the application. Eg: ["authorization_code"]' }).default(["authorization_code"]).optional(), + response_types: z.array(z.enum(["code", "token"])).meta({ description: 'The response types supported by the application. Eg: ["code"]' }).default(["code"]).optional(), + client_name: z.string().meta({ description: 'The name of the application. Eg: "My App"' }).optional(), + client_uri: z.string().meta({ description: 'The URI of the application. Eg: "https://client.example.com"' }).optional(), + logo_uri: z.string().meta({ description: 'The URI of the application logo. Eg: "https://client.example.com/logo.png"' }).optional(), + scope: z.string().meta({ description: 'The scopes supported by the application. Separated by spaces. Eg: "profile email"' }).optional(), + contacts: z.array(z.string()).meta({ description: 'The contact information for the application. Eg: ["admin@example.com"]' }).optional(), + tos_uri: z.string().meta({ description: 'The URI of the application terms of service. Eg: "https://client.example.com/tos"' }).optional(), + policy_uri: z.string().meta({ description: 'The URI of the application privacy policy. Eg: "https://client.example.com/policy"' }).optional(), + jwks_uri: z.string().meta({ description: 'The URI of the application JWKS. Eg: "https://client.example.com/jwks"' }).optional(), + jwks: z.record(z.any(), z.any()).meta({ description: 'The JWKS of the application. Eg: {"keys": [{"kty": "RSA", "alg": "RS256", "use": "sig", "n": "...", "e": "..."}]}' }).optional(), + metadata: z.record(z.any(), z.any()).meta({ description: 'The metadata of the application. Eg: {"key": "value"}' }).optional(), + software_id: z.string().meta({ description: 'The software ID of the application. Eg: "my-software"' }).optional(), + software_version: z.string().meta({ description: 'The software version of the application. Eg: "1.0.0"' }).optional(), + software_statement: z.string().meta({ description: "The software statement of the application." }).optional() +}); +z.object({ + redirect_uris: z.array(z.string()), + token_endpoint_auth_method: z.enum([ + "none", + "client_secret_basic", + "client_secret_post" + ]).default("client_secret_basic").optional(), + grant_types: z.array(z.enum([ + "authorization_code", + "implicit", + "password", + "client_credentials", + "refresh_token", + "urn:ietf:params:oauth:grant-type:jwt-bearer", + "urn:ietf:params:oauth:grant-type:saml2-bearer" + ])).default(["authorization_code"]).optional(), + response_types: z.array(z.enum(["code", "token"])).default(["code"]).optional(), + client_name: z.string().optional(), + client_uri: z.string().optional(), + logo_uri: z.string().optional(), + scope: z.string().optional(), + contacts: z.array(z.string()).optional(), + tos_uri: z.string().optional(), + policy_uri: z.string().optional(), + jwks_uri: z.string().optional(), + jwks: z.record(z.string(), z.any()).optional(), + metadata: z.record(z.any(), z.any()).optional(), + software_id: z.string().optional(), + software_version: z.string().optional(), + software_statement: z.string().optional() +}); +z.record(z.any(), z.any()); +defineErrorCodes({ INVALID_SESSION_TOKEN: "Invalid session token" }); +z.object({ sessionToken: z.string().meta({ description: "The session token to set as active" }) }); +z.object({ sessionToken: z.string().meta({ description: "The session token to revoke" }) }); +z.object({ + callbackURL: z.string().meta({ description: "The URL to redirect to after the proxy" }), + cookies: z.string().meta({ description: "The cookies to set after the proxy" }) +}); +z.object({ idToken: z.string().meta({ description: "Google ID token, which the client obtains from the One Tap API" }) }); +z.object({ token: z.string().meta({ description: 'The token to verify. Eg: "some-token"' }) }); +createAuthMiddleware(async () => { + return {}; +}); +createAuthMiddleware({ use: [sessionMiddleware] }, async (ctx) => { + return { session: ctx.context.session }; +}); +defineErrorCodes({ + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION: "You are not allowed to create a new organization", + YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS: "You have reached the maximum number of organizations", + ORGANIZATION_ALREADY_EXISTS: "Organization already exists", + ORGANIZATION_SLUG_ALREADY_TAKEN: "Organization slug already taken", + ORGANIZATION_NOT_FOUND: "Organization not found", + USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION: "User is not a member of the organization", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION: "You are not allowed to update this organization", + YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION: "You are not allowed to delete this organization", + NO_ACTIVE_ORGANIZATION: "No active organization", + USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION: "User is already a member of this organization", + MEMBER_NOT_FOUND: "Member not found", + ROLE_NOT_FOUND: "Role not found", + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM: "You are not allowed to create a new team", + TEAM_ALREADY_EXISTS: "Team already exists", + TEAM_NOT_FOUND: "Team not found", + YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER: "You cannot leave the organization as the only owner", + YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER: "You cannot leave the organization without an owner", + YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER: "You are not allowed to delete this member", + YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION: "You are not allowed to invite users to this organization", + USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION: "User is already invited to this organization", + INVITATION_NOT_FOUND: "Invitation not found", + YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION: "You are not the recipient of the invitation", + EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION: "Email verification required before accepting or rejecting invitation", + YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION: "You are not allowed to cancel this invitation", + INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION: "Inviter is no longer a member of the organization", + YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE: "You are not allowed to invite a user with this role", + FAILED_TO_RETRIEVE_INVITATION: "Failed to retrieve invitation", + YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS: "You have reached the maximum number of teams", + UNABLE_TO_REMOVE_LAST_TEAM: "Unable to remove last team", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER: "You are not allowed to update this member", + ORGANIZATION_MEMBERSHIP_LIMIT_REACHED: "Organization membership limit reached", + YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION: "You are not allowed to create teams in this organization", + YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION: "You are not allowed to delete teams in this organization", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM: "You are not allowed to update this team", + YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM: "You are not allowed to delete this team", + INVITATION_LIMIT_REACHED: "Invitation limit reached", + TEAM_MEMBER_LIMIT_REACHED: "Team member limit reached", + USER_IS_NOT_A_MEMBER_OF_THE_TEAM: "User is not a member of the team", + YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM: "You are not allowed to list the members of this team", + YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM: "You do not have an active team", + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER: "You are not allowed to create a new member", + YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER: "You are not allowed to remove a team member", + YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION: "You are not allowed to access this organization as an owner", + YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION: "You are not a member of this organization", + MISSING_AC_INSTANCE: "Dynamic Access Control requires a pre-defined ac instance on the server auth plugin. Read server logs for more information", + YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE: "You must be in an organization to create a role", + YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE: "You are not allowed to create a role", + YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE: "You are not allowed to update a role", + YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE: "You are not allowed to delete a role", + YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE: "You are not allowed to read a role", + YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE: "You are not allowed to list a role", + YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE: "You are not allowed to get a role", + TOO_MANY_ROLES: "This organization has too many roles", + INVALID_RESOURCE: "The provided permission includes an invalid resource", + ROLE_NAME_IS_ALREADY_TAKEN: "That role name is already taken", + CANNOT_DELETE_A_PRE_DEFINED_ROLE: "Cannot delete a pre-defined role", + ROLE_IS_ASSIGNED_TO_MEMBERS: "Cannot delete a role that is assigned to members. Please reassign the members to a different role first" +}); +z.object({ + organizationId: z.string().optional().meta({ description: "The id of the organization to create the role in. If not provided, the user's active organization will be used." }), + role: z.string().meta({ description: "The name of the role to create" }), + permission: z.record(z.string(), z.array(z.string())).meta({ description: "The permission to assign to the role" }) +}); +z.object({ organizationId: z.string().optional().meta({ description: "The id of the organization to create the role in. If not provided, the user's active organization will be used." }) }).and(z.union([z.object({ roleName: z.string().nonempty().meta({ description: "The name of the role to delete" }) }), z.object({ roleId: z.string().nonempty().meta({ description: "The id of the role to delete" }) })])); +z.object({ organizationId: z.string().optional().meta({ description: "The id of the organization to list roles for. If not provided, the user's active organization will be used." }) }).optional(); +z.object({ organizationId: z.string().optional().meta({ description: "The id of the organization to read a role for. If not provided, the user's active organization will be used." }) }).and(z.union([z.object({ roleName: z.string().nonempty().meta({ description: "The name of the role to read" }) }), z.object({ roleId: z.string().nonempty().meta({ description: "The id of the role to read" }) })])).optional(); +z.union([z.object({ roleName: z.string().nonempty().meta({ description: "The name of the role to update" }) }), z.object({ roleId: z.string().nonempty().meta({ description: "The id of the role to update" }) })]); +z.object({ + email: z.string().meta({ description: "The email address of the user to invite" }), + role: z.union([z.string().meta({ description: "The role to assign to the user" }), z.array(z.string().meta({ description: "The roles to assign to the user" }))]).meta({ description: 'The role(s) to assign to the user. It can be `admin`, `member`, owner. Eg: "member"' }), + organizationId: z.string().meta({ description: "The organization ID to invite the user to" }).optional(), + resend: z.boolean().meta({ description: "Resend the invitation email, if the user is already invited. Eg: true" }).optional(), + teamId: z.union([z.string().meta({ description: "The team ID to invite the user to" }).optional(), z.array(z.string()).meta({ description: "The team IDs to invite the user to" }).optional()]) +}); +z.object({ invitationId: z.string().meta({ description: "The ID of the invitation to accept" }) }); +z.object({ invitationId: z.string().meta({ description: "The ID of the invitation to reject" }) }); +z.object({ invitationId: z.string().meta({ description: "The ID of the invitation to cancel" }) }); +z.object({ id: z.string().meta({ description: "The ID of the invitation to get" }) }); +z.object({ organizationId: z.string().meta({ description: "The ID of the organization to list invitations for" }).optional() }).optional(); +z.object({ + userId: z.coerce.string().meta({ description: 'The user Id which represents the user to be added as a member. If `null` is provided, then it\'s expected to provide session headers. Eg: "user-id"' }), + role: z.union([z.string(), z.array(z.string())]).meta({ description: 'The role(s) to assign to the new member. Eg: ["admin", "sale"]' }), + organizationId: z.string().meta({ description: `An optional organization ID to pass. If not provided, will default to the user's active organization. Eg: "org-id"` }).optional(), + teamId: z.string().meta({ description: 'An optional team ID to add the member to. Eg: "team-id"' }).optional() +}); +z.object({ + memberIdOrEmail: z.string().meta({ description: "The ID or email of the member to remove" }), + organizationId: z.string().meta({ description: 'The ID of the organization to remove the member from. If not provided, the active organization will be used. Eg: "org-id"' }).optional() +}); +z.object({ + role: z.union([z.string(), z.array(z.string())]).meta({ description: 'The new role to be applied. This can be a string or array of strings representing the roles. Eg: ["admin", "sale"]' }), + memberId: z.string().meta({ description: 'The member id to apply the role update to. Eg: "member-id"' }), + organizationId: z.string().meta({ description: 'An optional organization ID which the member is a part of to apply the role update. If not provided, you must provide session headers to get the active organization. Eg: "organization-id"' }).optional() +}); +z.object({ organizationId: z.string().meta({ description: 'The organization Id for the member to leave. Eg: "organization-id"' }) }); +z.object({ + userId: z.string().meta({ description: "The user ID to get the role for. If not provided, will default to the current user's" }).optional(), + organizationId: z.string().meta({ description: `The organization ID to list members for. If not provided, will default to the user's active organization. Eg: "organization-id"` }).optional(), + organizationSlug: z.string().meta({ description: `The organization slug to list members for. If not provided, will default to the user's active organization. Eg: "organization-slug"` }).optional() +}).optional(); +z.object({ + name: z.string().min(1).meta({ description: "The name of the organization" }), + slug: z.string().min(1).meta({ description: "The slug of the organization" }), + userId: z.coerce.string().meta({ description: 'The user id of the organization creator. If not provided, the current user will be used. Should only be used by admins or when called by the server. server-only. Eg: "user-id"' }).optional(), + logo: z.string().meta({ description: "The logo of the organization" }).optional(), + metadata: z.record(z.string(), z.any()).meta({ description: "The metadata of the organization" }).optional(), + keepCurrentActiveOrganization: z.boolean().meta({ description: "Whether to keep the current active organization active after creating a new one. Eg: true" }).optional() +}); +z.object({ slug: z.string().meta({ description: 'The organization slug to check. Eg: "my-org"' }) }); +z.object({ + name: z.string().min(1).meta({ description: "The name of the organization" }).optional(), + slug: z.string().min(1).meta({ description: "The slug of the organization" }).optional(), + logo: z.string().meta({ description: "The logo of the organization" }).optional(), + metadata: z.record(z.string(), z.any()).meta({ description: "The metadata of the organization" }).optional() +}); +z.object({ organizationId: z.string().meta({ description: "The organization id to delete" }) }); +z.optional(z.object({ + organizationId: z.string().meta({ description: "The organization id to get" }).optional(), + organizationSlug: z.string().meta({ description: "The organization slug to get" }).optional(), + membersLimit: z.number().or(z.string().transform((val) => parseInt(val))).meta({ description: "The limit of members to get. By default, it uses the membershipLimit option which defaults to 100." }).optional() +})); +z.object({ + organizationId: z.string().meta({ description: 'The organization id to set as active. It can be null to unset the active organization. Eg: "org-id"' }).nullable().optional(), + organizationSlug: z.string().meta({ description: 'The organization slug to set as active. It can be null to unset the active organization if organizationId is not provided. Eg: "org-slug"' }).optional() +}); +const roleSchema = z.string(); +const invitationStatus = z.enum([ + "pending", + "accepted", + "rejected", + "canceled" +]).default("pending"); +z.object({ + id: z.string().default(generateId), + name: z.string(), + slug: z.string(), + logo: z.string().nullish().optional(), + metadata: z.record(z.string(), z.unknown()).or(z.string().transform((v) => JSON.parse(v))).optional(), + createdAt: z.date() +}); +z.object({ + id: z.string().default(generateId), + organizationId: z.string(), + userId: z.coerce.string(), + role: roleSchema, + createdAt: z.date().default(() => /* @__PURE__ */ new Date()) +}); +z.object({ + id: z.string().default(generateId), + organizationId: z.string(), + email: z.string(), + role: roleSchema, + status: invitationStatus, + teamId: z.string().nullish(), + inviterId: z.string(), + expiresAt: z.date(), + createdAt: z.date().default(() => /* @__PURE__ */ new Date()) +}); +z.object({ + id: z.string().default(generateId), + name: z.string().min(1), + organizationId: z.string(), + createdAt: z.date(), + updatedAt: z.date().optional() +}); +z.object({ + id: z.string().default(generateId), + teamId: z.string(), + userId: z.string(), + createdAt: z.date().default(() => /* @__PURE__ */ new Date()) +}); +z.object({ + id: z.string().default(generateId), + organizationId: z.string(), + role: z.string(), + permission: z.record(z.string(), z.array(z.string())), + createdAt: z.date().default(() => /* @__PURE__ */ new Date()), + updatedAt: z.date().optional() +}); +const defaultRoles = [ + "admin", + "member", + "owner" +]; +z.union([z.enum(defaultRoles), z.array(z.enum(defaultRoles))]); +z.object({ + name: z.string().meta({ description: 'The name of the team. Eg: "my-team"' }), + organizationId: z.string().meta({ description: 'The organization ID which the team will be created in. Defaults to the active organization. Eg: "organization-id"' }).optional() +}); +z.object({ + teamId: z.string().meta({ description: `The team ID of the team to remove. Eg: "team-id"` }), + organizationId: z.string().meta({ description: `The organization ID which the team falls under. If not provided, it will default to the user's active organization. Eg: "organization-id"` }).optional() +}); +z.optional(z.object({ organizationId: z.string().meta({ description: `The organization ID which the teams are under to list. Defaults to the users active organization. Eg: "organization-id"` }).optional() })); +z.object({ teamId: z.string().meta({ description: "The team id to set as active. It can be null to unset the active team" }).nullable().optional() }); +z.optional(z.object({ teamId: z.string().optional().meta({ description: "The team whose members we should return. If this is not provided the members of the current active team get returned." }) })); +z.object({ + teamId: z.string().meta({ description: "The team the user should be a member of." }), + userId: z.coerce.string().meta({ description: "The user Id which represents the user to be added as a member." }) +}); +z.object({ + teamId: z.string().meta({ description: "The team the user should be removed from." }), + userId: z.coerce.string().meta({ description: "The user which should be removed from the team." }) +}); +z.object({ organizationId: z.string().optional() }).and(z.union([z.object({ + permission: z.record(z.string(), z.array(z.string())), + permissions: z.undefined() +}), z.object({ + permission: z.undefined(), + permissions: z.record(z.string(), z.array(z.string())) +})])); +defineErrorCodes({ + INVALID_PHONE_NUMBER: "Invalid phone number", + PHONE_NUMBER_EXIST: "Phone number already exists", + PHONE_NUMBER_NOT_EXIST: "phone number isn't registered", + INVALID_PHONE_NUMBER_OR_PASSWORD: "Invalid phone number or password", + UNEXPECTED_ERROR: "Unexpected error", + OTP_NOT_FOUND: "OTP not found", + OTP_EXPIRED: "OTP expired", + INVALID_OTP: "Invalid OTP", + PHONE_NUMBER_NOT_VERIFIED: "Phone number not verified", + PHONE_NUMBER_CANNOT_BE_UPDATED: "Phone number cannot be updated", + SEND_OTP_NOT_IMPLEMENTED: "sendOTP not implemented", + TOO_MANY_ATTEMPTS: "Too many attempts" +}); +z.object({ + phoneNumber: z.string().meta({ description: 'Phone number to sign in. Eg: "+1234567890"' }), + password: z.string().meta({ description: "Password to use for sign in." }), + rememberMe: z.boolean().meta({ description: "Remember the session. Eg: true" }).optional() +}); +z.object({ phoneNumber: z.string().meta({ description: 'Phone number to send OTP. Eg: "+1234567890"' }) }); +z.object({ + phoneNumber: z.string().meta({ description: 'Phone number to verify. Eg: "+1234567890"' }), + code: z.string().meta({ description: 'OTP code. Eg: "123456"' }), + disableSession: z.boolean().meta({ description: "Disable session creation after verification. Eg: false" }).optional(), + updatePhoneNumber: z.boolean().meta({ description: "Check if there is a session and update the phone number. Eg: true" }).optional() +}).and(z.record(z.string(), z.any())); +z.object({ phoneNumber: z.string() }); +z.object({ + otp: z.string().meta({ description: 'The one time password to reset the password. Eg: "123456"' }), + phoneNumber: z.string().meta({ description: 'The phone number to the account which intends to reset the password for. Eg: "+1234567890"' }), + newPassword: z.string().meta({ description: `The new password. Eg: "new-and-secure-password"` }) +}); +z.object({ + walletAddress: z.string().regex(/^0[xX][a-fA-F0-9]{40}$/i).length(42), + chainId: z.number().int().positive().max(2147483647).optional().default(1) +}); +defineErrorCodes({ + OTP_NOT_ENABLED: "OTP not enabled", + OTP_HAS_EXPIRED: "OTP has expired", + TOTP_NOT_ENABLED: "TOTP not enabled", + TWO_FACTOR_NOT_ENABLED: "Two factor isn't enabled", + BACKUP_CODES_NOT_ENABLED: "Backup codes aren't enabled", + INVALID_BACKUP_CODE: "Invalid backup code", + INVALID_CODE: "Invalid code", + TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: "Too many attempts. Please request a new code.", + INVALID_TWO_FACTOR_COOKIE: "Invalid two factor cookie" +}); +z.object({ + code: z.string().meta({ description: `A backup code to verify. Eg: "123456"` }), + disableSession: z.boolean().meta({ description: "If true, the session cookie will not be set." }).optional(), + trustDevice: z.boolean().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional() +}); +z.object({ userId: z.coerce.string().meta({ description: `The user ID to view all backup codes. Eg: "user-id"` }) }); +z.object({ password: z.string().meta({ description: "The users password." }) }); +z.object({ + code: z.string().meta({ description: 'The otp code to verify. Eg: "012345"' }), + trustDevice: z.boolean().optional().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }) +}); +z.object({ trustDevice: z.boolean().optional().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }) }).optional(); +z.object({ secret: z.string().meta({ description: "The secret to generate the TOTP code" }) }); +z.object({ password: z.string().meta({ description: "User password" }) }); +z.object({ + code: z.string().meta({ description: 'The otp code to verify. Eg: "012345"' }), + trustDevice: z.boolean().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional() +}); +z.object({ + password: z.string().meta({ description: "User password" }), + issuer: z.string().meta({ description: "Custom issuer for the TOTP URI" }).optional() +}); +z.object({ password: z.string().meta({ description: "User password" }) }); +defineErrorCodes({ + INVALID_USERNAME_OR_PASSWORD: "Invalid username or password", + EMAIL_NOT_VERIFIED: "Email not verified", + UNEXPECTED_ERROR: "Unexpected error", + USERNAME_IS_ALREADY_TAKEN: "Username is already taken. Please try another.", + USERNAME_TOO_SHORT: "Username is too short", + USERNAME_TOO_LONG: "Username is too long", + INVALID_USERNAME: "Username is invalid", + INVALID_DISPLAY_USERNAME: "Display username is invalid" +}); +z.object({ + username: z.string().meta({ description: "The username of the user" }), + password: z.string().meta({ description: "The password of the user" }), + rememberMe: z.boolean().meta({ description: "Remember the user session" }).optional(), + callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional() +}); +z.object({ username: z.string().meta({ description: "The username to check" }) }); +const auth = betterAuth({ + baseURL: private_env.ORIGIN, + secret: private_env.BETTER_AUTH_SECRET, + database: drizzleAdapter(db, { provider: "pg" }), + emailAndPassword: { enabled: true }, + databaseHooks: { + user: { + create: { + after: async (user) => { + const [personalTeam] = await db.insert(team).values({ + name: user.name, + isPersonal: true, + ownerId: user.id, + avatarUrl: user.image + }).returning(); + await db.insert(teamMember).values({ + teamId: personalTeam.id, + userId: user.id, + role: "owner" + }); + } + } + } + }, + plugins: [ + genericOAuth({ + config: [ + { + providerId: "osu", + clientId: private_env.OSU_CLIENT_ID, + clientSecret: private_env.OSU_CLIENT_SECRET, + authorizationUrl: "https://osu.ppy.sh/oauth/authorize", + tokenUrl: "https://osu.ppy.sh/oauth/token", + userInfoUrl: "https://osu.ppy.sh/api/v2/me", + scopes: ["identify", "public"], + pkce: false, + authentication: "post", + // ── FIX: Use ORIGIN env var instead of hardcoded localhost ── + redirectURI: `${private_env.ORIGIN}/api/auth/oauth2/callback/osu`, + getToken: async ({ code, redirectURI }) => { + const res = await fetch("https://osu.ppy.sh/oauth/token", { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + client_id: private_env.OSU_CLIENT_ID, + client_secret: private_env.OSU_CLIENT_SECRET, + code, + grant_type: "authorization_code", + redirect_uri: redirectURI + }) + }); + const data = await res.json(); + if (!res.ok) { + console.error("osu! token exchange failed:", data); + throw new Error("Token exchange failed"); + } + return { + accessToken: data.access_token, + refreshToken: data.refresh_token, + accessTokenExpiresAt: new Date(Date.now() + data.expires_in * 1e3), + scopes: ["identify", "public"], + raw: data + }; + }, + mapProfileToUser(profile) { + return { + name: profile.username, + image: profile.avatar_url, + email: `${profile.id}@osu.local` + }; + } + } + ] + }), + sveltekitCookies(getRequestEvent) + // must be last + ] +}); +export { + auth as a, + createKyselyAdapter as c, + getKyselyDatabaseType as g, + svelteKitHandler as s +}; diff --git a/.svelte-kit/output/server/chunks/bun-sqlite-dialect.js b/.svelte-kit/output/server/chunks/bun-sqlite-dialect.js new file mode 100644 index 0000000..6fe94c6 --- /dev/null +++ b/.svelte-kit/output/server/chunks/bun-sqlite-dialect.js @@ -0,0 +1,155 @@ +import { CompiledQuery, DefaultQueryCompiler, DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE, sql } from "kysely"; +var BunSqliteAdapter = class { + get supportsCreateIfNotExists() { + return true; + } + get supportsTransactionalDdl() { + return false; + } + get supportsReturning() { + return true; + } + async acquireMigrationLock() { + } + async releaseMigrationLock() { + } + get supportsOutput() { + return true; + } +}; +var BunSqliteDriver = class { + #config; + #connectionMutex = new ConnectionMutex(); + #db; + #connection; + constructor(config) { + this.#config = { ...config }; + } + async init() { + this.#db = this.#config.database; + this.#connection = new BunSqliteConnection(this.#db); + if (this.#config.onCreateConnection) await this.#config.onCreateConnection(this.#connection); + } + async acquireConnection() { + await this.#connectionMutex.lock(); + return this.#connection; + } + async beginTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("begin")); + } + async commitTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("commit")); + } + async rollbackTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("rollback")); + } + async releaseConnection() { + this.#connectionMutex.unlock(); + } + async destroy() { + this.#db?.close(); + } +}; +var BunSqliteConnection = class { + #db; + constructor(db) { + this.#db = db; + } + executeQuery(compiledQuery) { + const { sql: sql$1, parameters } = compiledQuery; + const stmt = this.#db.prepare(sql$1); + return Promise.resolve({ rows: stmt.all(parameters) }); + } + async *streamQuery() { + throw new Error("Streaming query is not supported by SQLite driver."); + } +}; +var ConnectionMutex = class { + #promise; + #resolve; + async lock() { + while (await this.#promise) await this.#promise; + this.#promise = new Promise((resolve) => { + this.#resolve = resolve; + }); + } + unlock() { + const resolve = this.#resolve; + this.#promise = void 0; + this.#resolve = void 0; + resolve?.(); + } +}; +var BunSqliteIntrospector = class { + #db; + constructor(db) { + this.#db = db; + } + async getSchemas() { + return []; + } + async getTables(options = { withInternalKyselyTables: false }) { + let query = this.#db.selectFrom("sqlite_schema").where("type", "=", "table").where("name", "not like", "sqlite_%").select("name").$castTo(); + if (!options.withInternalKyselyTables) query = query.where("name", "!=", DEFAULT_MIGRATION_TABLE).where("name", "!=", DEFAULT_MIGRATION_LOCK_TABLE); + const tables = await query.execute(); + return Promise.all(tables.map(({ name }) => this.#getTableMetadata(name))); + } + async getMetadata(options) { + return { tables: await this.getTables(options) }; + } + async #getTableMetadata(table) { + const db = this.#db; + const autoIncrementCol = (await db.selectFrom("sqlite_master").where("name", "=", table).select("sql").$castTo().execute())[0]?.sql?.split(/[\(\),]/)?.find((it) => it.toLowerCase().includes("autoincrement"))?.split(/\s+/)?.[0]?.replace(/["`]/g, ""); + return { + name: table, + columns: (await db.selectFrom(sql`pragma_table_info(${table})`.as("table_info")).select([ + "name", + "type", + "notnull", + "dflt_value" + ]).execute()).map((col) => ({ + name: col.name, + dataType: col.type, + isNullable: !col.notnull, + isAutoIncrementing: col.name === autoIncrementCol, + hasDefaultValue: col.dflt_value != null + })), + isView: true + }; + } +}; +var BunSqliteQueryCompiler = class extends DefaultQueryCompiler { + getCurrentParameterPlaceholder() { + return "?"; + } + getLeftIdentifierWrapper() { + return '"'; + } + getRightIdentifierWrapper() { + return '"'; + } + getAutoIncrement() { + return "autoincrement"; + } +}; +var BunSqliteDialect = class { + #config; + constructor(config) { + this.#config = { ...config }; + } + createDriver() { + return new BunSqliteDriver(this.#config); + } + createQueryCompiler() { + return new BunSqliteQueryCompiler(); + } + createAdapter() { + return new BunSqliteAdapter(); + } + createIntrospector(db) { + return new BunSqliteIntrospector(db); + } +}; +export { + BunSqliteDialect +}; diff --git a/.svelte-kit/output/server/chunks/engine.js b/.svelte-kit/output/server/chunks/engine.js new file mode 100644 index 0000000..f6bafe4 --- /dev/null +++ b/.svelte-kit/output/server/chunks/engine.js @@ -0,0 +1,350 @@ +import { d as db, b as match, e as matchParticipant, t as teamMember, f as matchParticipantPlayer, g as matchGame, h as matchGameScore, i as matchQueue, p as playerRating } from "./index2.js"; +import { eq, sql, asc } from "drizzle-orm"; +const MATCH_STATES = { + CREATED: "CREATED", + LOBBY: "LOBBY", + ROLLING: "ROLLING", + PICKING: "PICKING", + PLAYING: "PLAYING", + FINISHED: "FINISHED", + CANCELLED: "CANCELLED" +}; +const GAME_STATES = { + PLAYING: "PLAYING", + FINISHED: "FINISHED" +}; +async function joinQueue(userId, teamId) { + let rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, userId) + }); + if (!rating) { + [rating] = await db.insert(playerRating).values({ userId, elo: 1e3 }).returning(); + } + const existing = await db.query.matchQueue.findFirst({ + where: eq(matchQueue.userId, userId) + }); + if (existing) throw new Error("Already in queue"); + await db.insert(matchQueue).values({ + userId, + teamId, + elo: rating.elo + }); + return tryMatchFromQueue(); +} +async function leaveQueue(userId) { + await db.delete(matchQueue).where(eq(matchQueue.userId, userId)); +} +async function getQueueStatus(userId) { + const entry = await db.query.matchQueue.findFirst({ + where: eq(matchQueue.userId, userId) + }); + const count = await db.select({ count: sql`count(*)` }).from(matchQueue); + return { + inQueue: !!entry, + queueSize: Number(count[0].count), + joinedAt: entry?.joinedAt ?? null + }; +} +async function tryMatchFromQueue() { + const queue = await db.query.matchQueue.findMany({ + orderBy: asc(matchQueue.elo) + }); + if (queue.length < 2) return null; + let bestPair = null; + let smallestGap = Infinity; + for (let i = 0; i < queue.length - 1; i++) { + const gap = Math.abs(queue[i].elo - queue[i + 1].elo); + if (gap < smallestGap) { + smallestGap = gap; + bestPair = [queue[i], queue[i + 1]]; + } + } + if (!bestPair) return null; + const avgElo = (bestPair[0].elo + bestPair[1].elo) / 2; + const selectedPool = await selectMappoolForRating(avgElo); + if (!selectedPool) throw new Error("No mappools available"); + await db.delete(matchQueue).where(eq(matchQueue.userId, bestPair[0].userId)); + await db.delete(matchQueue).where(eq(matchQueue.userId, bestPair[1].userId)); + const created = await createMatch({ + name: "Ranked Match", + config: { bestOf: 7, teamSize: 1, scoringType: "score" }, + mappoolId: selectedPool.id, + teams: [bestPair[0].teamId, bestPair[1].teamId], + createdBy: "system" + }); + return created; +} +async function selectMappoolForRating(avgElo) { + const pools = await db.query.mappool.findMany({ + with: { slots: true } + }); + if (pools.length === 0) return null; + const targetStars = 3 + (avgElo - 800) / 400 * 4; + const clampedTarget = Math.max(2, Math.min(8, targetStars)); + let bestPool = pools[0]; + let bestDiff = Infinity; + for (const pool of pools) { + if (pool.slots.length === 0) continue; + const avgStars = pool.slots.reduce((sum, s) => sum + (s.starRating ?? 0), 0) / pool.slots.length; + const diff = Math.abs(avgStars - clampedTarget); + if (diff < bestDiff) { + bestDiff = diff; + bestPool = pool; + } + } + return bestPool; +} +async function createMatch(opts) { + const { name, config, mappoolId, teams, createdBy } = opts; + const [created] = await db.insert(match).values({ + name, + config, + mappoolId, + createdBy, + state: MATCH_STATES.CREATED + }).returning(); + for (let i = 0; i < teams.length; i++) { + const [participant] = await db.insert(matchParticipant).values({ + matchId: created.id, + teamId: teams[i], + slot: i + 1 + }).returning(); + const members = await db.query.teamMember.findMany({ + where: eq(teamMember.teamId, teams[i]) + }); + for (const member of members) { + await db.insert(matchParticipantPlayer).values({ + participantId: participant.id, + userId: member.userId + }); + } + } + return getMatchFull(created.id); +} +async function moveToLobby(matchId, osuLobbyId) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.CREATED); + await db.update(match).set({ + state: MATCH_STATES.LOBBY, + osuLobbyId: osuLobbyId ?? null, + startedAt: /* @__PURE__ */ new Date() + }).where(eq(match.id, matchId)); + return getMatchFull(matchId); +} +async function moveToRolling(matchId) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.LOBBY); + await db.update(match).set({ state: MATCH_STATES.ROLLING }).where(eq(match.id, matchId)); + return getMatchFull(matchId); +} +async function submitRoll(matchId, participantId, value) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.ROLLING); + await db.update(matchParticipant).set({ rollValue: value }).where(eq(matchParticipant.id, participantId)); + const participants = await db.query.matchParticipant.findMany({ + where: eq(matchParticipant.matchId, matchId) + }); + const allRolled = participants.every( + (p) => p.id === participantId ? true : p.rollValue !== null + ); + if (allRolled) { + const sorted = [...participants].map((p) => ({ + ...p, + rollValue: p.id === participantId ? value : p.rollValue + })).sort((a, b) => b.rollValue - a.rollValue); + for (let i = 0; i < sorted.length; i++) { + await db.update(matchParticipant).set({ pickOrder: i + 1 }).where(eq(matchParticipant.id, sorted[i].id)); + } + await db.update(match).set({ state: MATCH_STATES.PICKING }).where(eq(match.id, matchId)); + } + return getMatchFull(matchId); +} +async function pickMap(matchId, participantId, mappoolSlotId) { + const m = await getMatchOrThrow(matchId); + assertState(m.state, MATCH_STATES.PICKING); + const participants = await db.query.matchParticipant.findMany({ + where: eq(matchParticipant.matchId, matchId) + }); + const totalGames = await db.query.matchGame.findMany({ + where: eq(matchGame.matchId, matchId) + }); + const expectedPicker = getExpectedPicker(participants, totalGames.length); + if (expectedPicker.id !== participantId) { + throw new Error("Not your turn to pick"); + } + const alreadyPlayed = totalGames.some((g) => g.mappoolSlotId === mappoolSlotId); + if (alreadyPlayed) { + throw new Error("This map has already been played"); + } + const [game] = await db.insert(matchGame).values({ + matchId, + gameNumber: totalGames.length + 1, + mappoolSlotId, + pickedByParticipantId: participantId, + state: GAME_STATES.PLAYING, + startedAt: /* @__PURE__ */ new Date() + }).returning(); + await db.update(match).set({ state: MATCH_STATES.PLAYING }).where(eq(match.id, matchId)); + return game; +} +async function submitGameScores(matchGameId, scores) { + const game = await db.query.matchGame.findFirst({ + where: eq(matchGame.id, matchGameId) + }); + if (!game) throw new Error("Game not found"); + if (game.state !== GAME_STATES.PLAYING) throw new Error(`Game is ${game.state}, not PLAYING`); + for (const s of scores) { + await db.insert(matchGameScore).values({ + matchGameId, + playerId: s.playerId, + score: s.score, + accuracy: s.accuracy ?? 0, + maxCombo: s.maxCombo ?? 0, + count300: s.count300 ?? 0, + count100: s.count100 ?? 0, + count50: s.count50 ?? 0, + countMiss: s.countMiss ?? 0, + mods: s.mods ?? [], + passed: s.passed ?? true + }); + } + const allScores = await db.query.matchGameScore.findMany({ + where: eq(matchGameScore.matchGameId, matchGameId), + with: { player: true } + }); + const participantScores = /* @__PURE__ */ new Map(); + for (const s of allScores) { + const pid = s.player.participantId; + participantScores.set(pid, (participantScores.get(pid) ?? 0) + s.score); + } + let winnerId = null; + let highScore = -1; + for (const [pid, total] of participantScores) { + if (total > highScore) { + highScore = total; + winnerId = pid; + } + } + await db.update(matchGame).set({ + state: GAME_STATES.FINISHED, + winnerParticipantId: winnerId, + finishedAt: /* @__PURE__ */ new Date() + }).where(eq(matchGame.id, matchGameId)); + if (winnerId) { + const participant = await db.query.matchParticipant.findFirst({ + where: eq(matchParticipant.id, winnerId) + }); + if (participant) { + await db.update(matchParticipant).set({ score: participant.score + 1 }).where(eq(matchParticipant.id, winnerId)); + } + } + const m = await getMatchOrThrow(game.matchId); + const config = m.config; + const winsNeeded = Math.ceil(config.bestOf / 2); + const participants = await db.query.matchParticipant.findMany({ + where: eq(matchParticipant.matchId, game.matchId) + }); + const matchWinner = participants.find((p) => p.score >= winsNeeded); + if (matchWinner) { + await db.update(match).set({ + state: MATCH_STATES.FINISHED, + winnerId: matchWinner.teamId, + finishedAt: /* @__PURE__ */ new Date() + }).where(eq(match.id, game.matchId)); + await updateElo(participants, matchWinner.id); + } else { + await db.update(match).set({ state: MATCH_STATES.PICKING }).where(eq(match.id, game.matchId)); + } + return getMatchFull(game.matchId); +} +async function updateElo(participants, winnerId) { + for (const p of participants) { + const players = await db.query.matchParticipantPlayer.findMany({ + where: eq(matchParticipantPlayer.participantId, p.id) + }); + const isWinner = p.id === winnerId; + for (const player of players) { + let rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, player.userId) + }); + if (!rating) { + [rating] = await db.insert(playerRating).values({ userId: player.userId, elo: 1e3 }).returning(); + } + const K = 32; + const expected = 0.5; + const actual = isWinner ? 1 : 0; + const newElo = Math.round(rating.elo + K * (actual - expected)); + await db.update(playerRating).set({ + elo: newElo, + wins: isWinner ? rating.wins + 1 : rating.wins, + losses: isWinner ? rating.losses : rating.losses + 1, + updatedAt: /* @__PURE__ */ new Date() + }).where(eq(playerRating.userId, player.userId)); + } + } +} +async function cancelMatch(matchId) { + await db.update(match).set({ + state: MATCH_STATES.CANCELLED, + finishedAt: /* @__PURE__ */ new Date() + }).where(eq(match.id, matchId)); + return getMatchFull(matchId); +} +function getExpectedPicker(participants, gameCount) { + const sorted = [...participants].sort((a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99)); + const pickerIndex = gameCount % sorted.length; + return sorted[pickerIndex]; +} +async function getMatchFull(matchId) { + const m = await db.query.match.findFirst({ + where: eq(match.id, matchId), + with: { + mappool: { + with: { slots: true } + }, + participants: { + with: { + team: true, + players: true + } + }, + games: { + with: { + slot: true, + scores: { + with: { player: true } + } + }, + orderBy: (g, { asc: asc2 }) => [asc2(g.gameNumber)] + } + } + }); + if (!m) throw new Error("Match not found"); + return m; +} +async function getMatchOrThrow(matchId) { + const m = await db.query.match.findFirst({ + where: eq(match.id, matchId) + }); + if (!m) throw new Error("Match not found"); + return m; +} +function assertState(current, expected) { + if (current !== expected) { + throw new Error(`Match is ${current}, expected ${expected}`); + } +} +export { + MATCH_STATES as M, + cancelMatch as a, + moveToRolling as b, + createMatch as c, + submitGameScores as d, + getQueueStatus as e, + getMatchFull as g, + joinQueue as j, + leaveQueue as l, + moveToLobby as m, + pickMap as p, + submitRoll as s +}; diff --git a/.svelte-kit/output/server/chunks/environment.js b/.svelte-kit/output/server/chunks/environment.js new file mode 100644 index 0000000..d81356b --- /dev/null +++ b/.svelte-kit/output/server/chunks/environment.js @@ -0,0 +1,14 @@ +let building = false; +let prerendering = false; +function set_building() { + building = true; +} +function set_prerendering() { + prerendering = true; +} +export { + set_prerendering as a, + building as b, + prerendering as p, + set_building as s +}; diff --git a/.svelte-kit/output/server/chunks/exports.js b/.svelte-kit/output/server/chunks/exports.js new file mode 100644 index 0000000..441ec91 --- /dev/null +++ b/.svelte-kit/output/server/chunks/exports.js @@ -0,0 +1,70 @@ +function validator(expected) { + function validate(module, file) { + if (!module) return; + for (const key in module) { + if (key[0] === "_" || expected.has(key)) continue; + const values = [...expected.values()]; + const hint = hint_for_supported_files(key, file?.slice(file.lastIndexOf("."))) ?? `valid exports are ${values.join(", ")}, or anything with a '_' prefix`; + throw new Error(`Invalid export '${key}'${file ? ` in ${file}` : ""} (${hint})`); + } + } + return validate; +} +function hint_for_supported_files(key, ext = ".js") { + const supported_files = []; + if (valid_layout_exports.has(key)) { + supported_files.push(`+layout${ext}`); + } + if (valid_page_exports.has(key)) { + supported_files.push(`+page${ext}`); + } + if (valid_layout_server_exports.has(key)) { + supported_files.push(`+layout.server${ext}`); + } + if (valid_page_server_exports.has(key)) { + supported_files.push(`+page.server${ext}`); + } + if (valid_server_exports.has(key)) { + supported_files.push(`+server${ext}`); + } + if (supported_files.length > 0) { + return `'${key}' is a valid export in ${supported_files.slice(0, -1).join(", ")}${supported_files.length > 1 ? " or " : ""}${supported_files.at(-1)}`; + } +} +const valid_layout_exports = /* @__PURE__ */ new Set([ + "load", + "prerender", + "csr", + "ssr", + "trailingSlash", + "config" +]); +const valid_page_exports = /* @__PURE__ */ new Set([...valid_layout_exports, "entries"]); +const valid_layout_server_exports = /* @__PURE__ */ new Set([...valid_layout_exports]); +const valid_page_server_exports = /* @__PURE__ */ new Set([...valid_layout_server_exports, "actions", "entries"]); +const valid_server_exports = /* @__PURE__ */ new Set([ + "GET", + "POST", + "PATCH", + "PUT", + "DELETE", + "OPTIONS", + "HEAD", + "fallback", + "prerender", + "trailingSlash", + "config", + "entries" +]); +const validate_layout_exports = validator(valid_layout_exports); +const validate_page_exports = validator(valid_page_exports); +const validate_layout_server_exports = validator(valid_layout_server_exports); +const validate_page_server_exports = validator(valid_page_server_exports); +const validate_server_exports = validator(valid_server_exports); +export { + validate_layout_exports as a, + validate_page_server_exports as b, + validate_page_exports as c, + validate_server_exports as d, + validate_layout_server_exports as v +}; diff --git a/.svelte-kit/output/server/chunks/false.js b/.svelte-kit/output/server/chunks/false.js new file mode 100644 index 0000000..f3effd6 --- /dev/null +++ b/.svelte-kit/output/server/chunks/false.js @@ -0,0 +1,4 @@ +const DEV = false; +export { + DEV as D +}; diff --git a/.svelte-kit/output/server/chunks/index.js b/.svelte-kit/output/server/chunks/index.js new file mode 100644 index 0000000..14b637b --- /dev/null +++ b/.svelte-kit/output/server/chunks/index.js @@ -0,0 +1,60 @@ +import { n as noop } from "./index3.js"; +import { s as safe_not_equal } from "./root.js"; +import "clsx"; +const subscriber_queue = []; +function readable(value, start) { + return { + subscribe: writable(value, start).subscribe + }; +} +function writable(value, start = noop) { + let stop = null; + const subscribers = /* @__PURE__ */ new Set(); + function set(new_value) { + if (safe_not_equal(value, new_value)) { + value = new_value; + if (stop) { + const run_queue = !subscriber_queue.length; + for (const subscriber of subscribers) { + subscriber[1](); + subscriber_queue.push(subscriber, value); + } + if (run_queue) { + for (let i = 0; i < subscriber_queue.length; i += 2) { + subscriber_queue[i][0](subscriber_queue[i + 1]); + } + subscriber_queue.length = 0; + } + } + } + } + function update(fn) { + set(fn( + /** @type {T} */ + value + )); + } + function subscribe(run, invalidate = noop) { + const subscriber = [run, invalidate]; + subscribers.add(subscriber); + if (subscribers.size === 1) { + stop = start(set, update) || noop; + } + run( + /** @type {T} */ + value + ); + return () => { + subscribers.delete(subscriber); + if (subscribers.size === 0 && stop) { + stop(); + stop = null; + } + }; + } + return { set, update, subscribe }; +} +export { + readable as r, + writable as w +}; diff --git a/.svelte-kit/output/server/chunks/index2.js b/.svelte-kit/output/server/chunks/index2.js new file mode 100644 index 0000000..2c507a6 --- /dev/null +++ b/.svelte-kit/output/server/chunks/index2.js @@ -0,0 +1,302 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; +import { pgTable, timestamp, text, boolean, index, uuid, real, integer, jsonb, uniqueIndex } from "drizzle-orm/pg-core"; +import { relations } from "drizzle-orm"; +import { b as private_env } from "./shared-server.js"; +const user = pgTable("user", { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: boolean("email_verified").default(false).notNull(), + image: text("image"), + // ── Staff role: 'player' | 'referee' | 'admin' ── + role: text("role").default("player").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => /* @__PURE__ */ new Date()).notNull() +}); +const session = pgTable( + "session", + { + id: text("id").primaryKey(), + expiresAt: timestamp("expires_at").notNull(), + token: text("token").notNull().unique(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").$onUpdate(() => /* @__PURE__ */ new Date()).notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }) + }, + (table) => [index("session_userId_idx").on(table.userId)] +); +const account = pgTable( + "account", + { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: timestamp("access_token_expires_at"), + refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), + scope: text("scope"), + password: text("password"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").$onUpdate(() => /* @__PURE__ */ new Date()).notNull() + }, + (table) => [index("account_userId_idx").on(table.userId)] +); +const verification = pgTable( + "verification", + { + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: timestamp("expires_at").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => /* @__PURE__ */ new Date()).notNull() + }, + (table) => [index("verification_identifier_idx").on(table.identifier)] +); +const userRelations = relations(user, ({ many }) => ({ + sessions: many(session), + accounts: many(account) +})); +const sessionRelations = relations(session, ({ one }) => ({ + user: one(user, { + fields: [session.userId], + references: [user.id] + }) +})); +const accountRelations = relations(account, ({ one }) => ({ + user: one(user, { + fields: [account.userId], + references: [user.id] + }) +})); +const team = pgTable("team", { + id: uuid("id").primaryKey().defaultRandom(), + name: text("name").notNull(), + isPersonal: boolean("is_personal").default(false).notNull(), + ownerId: text("owner_id"), + avatarUrl: text("avatar_url"), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull() +}); +const teamMember = pgTable("team_member", { + id: uuid("id").primaryKey().defaultRandom(), + teamId: uuid("team_id").notNull().references(() => team.id, { onDelete: "cascade" }), + userId: text("user_id").notNull(), + role: text("role").default("member"), + joinedAt: timestamp("joined_at", { withTimezone: true }).defaultNow() +}); +const mappool = pgTable("mappool", { + id: uuid("id").primaryKey().defaultRandom(), + name: text("name").notNull(), + createdBy: text("created_by"), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull() +}); +const mappoolSlot = pgTable("mappool_slot", { + id: uuid("id").primaryKey().defaultRandom(), + mappoolId: uuid("mappool_id").notNull().references(() => mappool.id, { onDelete: "cascade" }), + category: text("category").notNull(), + orderInCategory: integer("order_in_category").notNull(), + beatmapId: text("beatmap_id").notNull(), + starRating: real("star_rating"), + mods: text("mods").array().default([]).notNull() +}); +const match = pgTable( + "match", + { + id: uuid("id").primaryKey().defaultRandom(), + name: text("name"), + state: text("state").default("CREATED").notNull(), + config: jsonb("config").notNull(), + mappoolId: uuid("mappool_id").references(() => mappool.id), + osuLobbyId: integer("osu_lobby_id"), + scheduledAt: timestamp("scheduled_at", { withTimezone: true }), + startedAt: timestamp("started_at", { withTimezone: true }), + finishedAt: timestamp("finished_at", { withTimezone: true }), + winnerId: uuid("winner_id").references(() => team.id), + createdBy: text("created_by"), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull() + }, + (t) => [index("idx_match_state").on(t.state)] +); +const matchParticipant = pgTable( + "match_participant", + { + id: uuid("id").primaryKey().defaultRandom(), + matchId: uuid("match_id").notNull().references(() => match.id, { onDelete: "cascade" }), + teamId: uuid("team_id").notNull().references(() => team.id), + slot: integer("slot").notNull(), + score: integer("score").default(0).notNull(), + rollValue: integer("roll_value"), + pickOrder: integer("pick_order") + }, + (t) => [ + index("idx_mp_match_team").on(t.matchId, t.teamId), + uniqueIndex("idx_mp_match_slot").on(t.matchId, t.slot), + index("idx_mp_match").on(t.matchId) + ] +); +const matchParticipantPlayer = pgTable( + "match_participant_player", + { + id: uuid("id").primaryKey().defaultRandom(), + participantId: uuid("participant_id").notNull().references(() => matchParticipant.id, { onDelete: "cascade" }), + userId: text("user_id").notNull() + }, + (t) => [ + uniqueIndex("idx_mpp_participant_user").on(t.participantId, t.userId), + index("idx_mpp_participant").on(t.participantId) + ] +); +const matchGame = pgTable( + "match_game", + { + id: uuid("id").primaryKey().defaultRandom(), + matchId: uuid("match_id").notNull().references(() => match.id, { onDelete: "cascade" }), + gameNumber: integer("game_number").notNull(), + mappoolSlotId: uuid("mappool_slot_id").notNull().references(() => mappoolSlot.id), + pickedByParticipantId: uuid("picked_by_participant_id").references(() => matchParticipant.id), + winnerParticipantId: uuid("winner_participant_id").references(() => matchParticipant.id), + state: text("state").default("PENDING").notNull(), + startedAt: timestamp("started_at", { withTimezone: true }), + finishedAt: timestamp("finished_at", { withTimezone: true }) + }, + (t) => [index("idx_match_game_match").on(t.matchId)] +); +const matchGameScore = pgTable( + "match_game_score", + { + id: uuid("id").primaryKey().defaultRandom(), + matchGameId: uuid("match_game_id").notNull().references(() => matchGame.id, { onDelete: "cascade" }), + playerId: uuid("player_id").notNull().references(() => matchParticipantPlayer.id), + score: integer("score").default(0).notNull(), + accuracy: real("accuracy").default(0).notNull(), + maxCombo: integer("max_combo").default(0).notNull(), + count300: integer("count_300").default(0).notNull(), + count100: integer("count_100").default(0).notNull(), + count50: integer("count_50").default(0).notNull(), + countMiss: integer("count_miss").default(0).notNull(), + mods: text("mods").array().default([]).notNull(), + passed: boolean("passed").default(false).notNull() + }, + (t) => [index("idx_mgs_game").on(t.matchGameId)] +); +const matchQueue = pgTable( + "match_queue", + { + id: uuid("id").primaryKey().defaultRandom(), + userId: text("user_id").notNull(), + teamId: uuid("team_id").notNull().references(() => team.id), + elo: integer("elo").default(1e3).notNull(), + joinedAt: timestamp("joined_at", { withTimezone: true }).defaultNow().notNull() + }, + (t) => [ + uniqueIndex("idx_queue_user").on(t.userId), + index("idx_queue_elo").on(t.elo) + ] +); +const playerRating = pgTable("player_rating", { + id: uuid("id").primaryKey().defaultRandom(), + userId: text("user_id").notNull().unique(), + elo: integer("elo").default(1e3).notNull(), + wins: integer("wins").default(0).notNull(), + losses: integer("losses").default(0).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull() +}); +const teamRelations = relations(team, ({ many }) => ({ + members: many(teamMember) +})); +const teamMemberRelations = relations(teamMember, ({ one }) => ({ + team: one(team, { fields: [teamMember.teamId], references: [team.id] }) +})); +const mappoolRelations = relations(mappool, ({ many }) => ({ + slots: many(mappoolSlot) +})); +const mappoolSlotRelations = relations(mappoolSlot, ({ one }) => ({ + mappool: one(mappool, { fields: [mappoolSlot.mappoolId], references: [mappool.id] }) +})); +const matchRelations = relations(match, ({ one, many }) => ({ + mappool: one(mappool, { fields: [match.mappoolId], references: [mappool.id] }), + participants: many(matchParticipant), + games: many(matchGame) +})); +const matchParticipantRelations = relations(matchParticipant, ({ one, many }) => ({ + match: one(match, { fields: [matchParticipant.matchId], references: [match.id] }), + team: one(team, { fields: [matchParticipant.teamId], references: [team.id] }), + players: many(matchParticipantPlayer) +})); +const matchParticipantPlayerRelations = relations(matchParticipantPlayer, ({ one }) => ({ + participant: one(matchParticipant, { + fields: [matchParticipantPlayer.participantId], + references: [matchParticipant.id] + }) +})); +const matchGameRelations = relations(matchGame, ({ one, many }) => ({ + match: one(match, { fields: [matchGame.matchId], references: [match.id] }), + slot: one(mappoolSlot, { fields: [matchGame.mappoolSlotId], references: [mappoolSlot.id] }), + scores: many(matchGameScore) +})); +const matchGameScoreRelations = relations(matchGameScore, ({ one }) => ({ + game: one(matchGame, { fields: [matchGameScore.matchGameId], references: [matchGame.id] }), + player: one(matchParticipantPlayer, { + fields: [matchGameScore.playerId], + references: [matchParticipantPlayer.id] + }) +})); +const matchQueueRelations = relations(matchQueue, ({ one }) => ({ + team: one(team, { fields: [matchQueue.teamId], references: [team.id] }) +})); +const schema = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + account, + accountRelations, + mappool, + mappoolRelations, + mappoolSlot, + mappoolSlotRelations, + match, + matchGame, + matchGameRelations, + matchGameScore, + matchGameScoreRelations, + matchParticipant, + matchParticipantPlayer, + matchParticipantPlayerRelations, + matchParticipantRelations, + matchQueue, + matchQueueRelations, + matchRelations, + playerRating, + session, + sessionRelations, + team, + teamMember, + teamMemberRelations, + teamRelations, + user, + userRelations, + verification +}, Symbol.toStringTag, { value: "Module" })); +if (!private_env.DATABASE_URL) throw new Error("DATABASE_URL is not set"); +const client = postgres(private_env.DATABASE_URL); +const db = drizzle(client, { schema }); +export { + mappoolSlot as a, + match as b, + team as c, + db as d, + matchParticipant as e, + matchParticipantPlayer as f, + matchGame as g, + matchGameScore as h, + matchQueue as i, + account as j, + mappool as m, + playerRating as p, + teamMember as t, + user as u +}; diff --git a/.svelte-kit/output/server/chunks/index3.js b/.svelte-kit/output/server/chunks/index3.js new file mode 100644 index 0000000..20b173b --- /dev/null +++ b/.svelte-kit/output/server/chunks/index3.js @@ -0,0 +1,1249 @@ +import { clsx as clsx$1 } from "clsx"; +import * as devalue from "devalue"; +var is_array = Array.isArray; +var index_of = Array.prototype.indexOf; +var includes = Array.prototype.includes; +var array_from = Array.from; +var define_property = Object.defineProperty; +var get_descriptor = Object.getOwnPropertyDescriptor; +var object_prototype = Object.prototype; +var array_prototype = Array.prototype; +var get_prototype_of = Object.getPrototypeOf; +var is_extensible = Object.isExtensible; +var has_own_property = Object.prototype.hasOwnProperty; +const noop = () => { +}; +function run_all(arr) { + for (var i = 0; i < arr.length; i++) { + arr[i](); + } +} +function deferred() { + var resolve; + var reject; + var promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +} +const DERIVED = 1 << 1; +const EFFECT = 1 << 2; +const RENDER_EFFECT = 1 << 3; +const MANAGED_EFFECT = 1 << 24; +const BLOCK_EFFECT = 1 << 4; +const BRANCH_EFFECT = 1 << 5; +const ROOT_EFFECT = 1 << 6; +const BOUNDARY_EFFECT = 1 << 7; +const CONNECTED = 1 << 9; +const CLEAN = 1 << 10; +const DIRTY = 1 << 11; +const MAYBE_DIRTY = 1 << 12; +const INERT = 1 << 13; +const DESTROYED = 1 << 14; +const REACTION_RAN = 1 << 15; +const EFFECT_TRANSPARENT = 1 << 16; +const EAGER_EFFECT = 1 << 17; +const HEAD_EFFECT = 1 << 18; +const EFFECT_PRESERVED = 1 << 19; +const USER_EFFECT = 1 << 20; +const WAS_MARKED = 1 << 16; +const REACTION_IS_UPDATING = 1 << 21; +const ASYNC = 1 << 22; +const ERROR_VALUE = 1 << 23; +const STATE_SYMBOL = /* @__PURE__ */ Symbol("$state"); +const LEGACY_PROPS = /* @__PURE__ */ Symbol("legacy props"); +const STALE_REACTION = new class StaleReactionError extends Error { + name = "StaleReactionError"; + message = "The reaction that called `getAbortSignal()` was re-run or destroyed"; +}(); +const COMMENT_NODE = 8; +function lifecycle_outside_component(name) { + { + throw new Error(`https://svelte.dev/e/lifecycle_outside_component`); + } +} +const HYDRATION_START = "["; +const HYDRATION_START_ELSE = "[!"; +const HYDRATION_START_FAILED = "[?"; +const HYDRATION_END = "]"; +const HYDRATION_ERROR = {}; +const ELEMENT_IS_NAMESPACED = 1; +const ELEMENT_PRESERVE_ATTRIBUTE_CASE = 1 << 1; +const ELEMENT_IS_INPUT = 1 << 2; +const UNINITIALIZED = /* @__PURE__ */ Symbol(); +const DOM_BOOLEAN_ATTRIBUTES = [ + "allowfullscreen", + "async", + "autofocus", + "autoplay", + "checked", + "controls", + "default", + "disabled", + "formnovalidate", + "indeterminate", + "inert", + "ismap", + "loop", + "multiple", + "muted", + "nomodule", + "novalidate", + "open", + "playsinline", + "readonly", + "required", + "reversed", + "seamless", + "selected", + "webkitdirectory", + "defer", + "disablepictureinpicture", + "disableremoteplayback" +]; +function is_boolean_attribute(name) { + return DOM_BOOLEAN_ATTRIBUTES.includes(name); +} +const PASSIVE_EVENTS = ["touchstart", "touchmove"]; +function is_passive_event(name) { + return PASSIVE_EVENTS.includes(name); +} +const ATTR_REGEX = /[&"<]/g; +const CONTENT_REGEX = /[&<]/g; +function escape_html(value, is_attr) { + const str = String(value ?? ""); + const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX; + pattern.lastIndex = 0; + let escaped = ""; + let last = 0; + while (pattern.test(str)) { + const i = pattern.lastIndex - 1; + const ch = str[i]; + escaped += str.substring(last, i) + (ch === "&" ? "&" : ch === '"' ? """ : "<"); + last = i + 1; + } + return escaped + str.substring(last); +} +const replacements = { + translate: /* @__PURE__ */ new Map([ + [true, "yes"], + [false, "no"] + ]) +}; +function attr(name, value, is_boolean = false) { + if (name === "hidden" && value !== "until-found") { + is_boolean = true; + } + if (value == null || !value && is_boolean) return ""; + const normalized = has_own_property.call(replacements, name) && replacements[name].get(value) || value; + const assignment = is_boolean ? `=""` : `="${escape_html(normalized, true)}"`; + return ` ${name}${assignment}`; +} +function clsx(value) { + if (typeof value === "object") { + return clsx$1(value); + } else { + return value ?? ""; + } +} +const whitespace = [..." \n\r\f \v\uFEFF"]; +function to_class(value, hash, directives) { + var classname = value == null ? "" : "" + value; + if (hash) { + classname = classname ? classname + " " + hash : hash; + } + if (directives) { + for (var key of Object.keys(directives)) { + if (directives[key]) { + classname = classname ? classname + " " + key : key; + } else if (classname.length) { + var len = key.length; + var a = 0; + while ((a = classname.indexOf(key, a)) >= 0) { + var b = a + len; + if ((a === 0 || whitespace.includes(classname[a - 1])) && (b === classname.length || whitespace.includes(classname[b]))) { + classname = (a === 0 ? "" : classname.substring(0, a)) + classname.substring(b + 1); + } else { + a = b; + } + } + } + } + } + return classname === "" ? null : classname; +} +function append_styles(styles, important = false) { + var separator = important ? " !important;" : ";"; + var css = ""; + for (var key of Object.keys(styles)) { + var value = styles[key]; + if (value != null && value !== "") { + css += " " + key + ": " + value + separator; + } + } + return css; +} +function to_css_name(name) { + if (name[0] !== "-" || name[1] !== "-") { + return name.toLowerCase(); + } + return name; +} +function to_style(value, styles) { + if (styles) { + var new_style = ""; + var normal_styles; + var important_styles; + if (Array.isArray(styles)) { + normal_styles = styles[0]; + important_styles = styles[1]; + } else { + normal_styles = styles; + } + if (value) { + value = String(value).replaceAll(/\s*\/\*.*?\*\/\s*/g, "").trim(); + var in_str = false; + var in_apo = 0; + var in_comment = false; + var reserved_names = []; + if (normal_styles) { + reserved_names.push(...Object.keys(normal_styles).map(to_css_name)); + } + if (important_styles) { + reserved_names.push(...Object.keys(important_styles).map(to_css_name)); + } + var start_index = 0; + var name_index = -1; + const len = value.length; + for (var i = 0; i < len; i++) { + var c = value[i]; + if (in_comment) { + if (c === "/" && value[i - 1] === "*") { + in_comment = false; + } + } else if (in_str) { + if (in_str === c) { + in_str = false; + } + } else if (c === "/" && value[i + 1] === "*") { + in_comment = true; + } else if (c === '"' || c === "'") { + in_str = c; + } else if (c === "(") { + in_apo++; + } else if (c === ")") { + in_apo--; + } + if (!in_comment && in_str === false && in_apo === 0) { + if (c === ":" && name_index === -1) { + name_index = i; + } else if (c === ";" || i === len - 1) { + if (name_index !== -1) { + var name = to_css_name(value.substring(start_index, name_index).trim()); + if (!reserved_names.includes(name)) { + if (c !== ";") { + i++; + } + var property = value.substring(start_index, i).trim(); + new_style += " " + property + ";"; + } + } + start_index = i + 1; + name_index = -1; + } + } + } + } + if (normal_styles) { + new_style += append_styles(normal_styles); + } + if (important_styles) { + new_style += append_styles(important_styles, true); + } + new_style = new_style.trim(); + return new_style === "" ? null : new_style; + } + return value == null ? null : String(value); +} +const BLOCK_OPEN = ``; +const BLOCK_CLOSE = ``; +let controller = null; +function abort() { + controller?.abort(STALE_REACTION); + controller = null; +} +function await_invalid() { + const error = new Error(`await_invalid +Encountered asynchronous work while rendering synchronously. +https://svelte.dev/e/await_invalid`); + error.name = "Svelte error"; + throw error; +} +function invalid_csp() { + const error = new Error(`invalid_csp +\`csp.nonce\` was set while \`csp.hash\` was \`true\`. These options cannot be used simultaneously. +https://svelte.dev/e/invalid_csp`); + error.name = "Svelte error"; + throw error; +} +function server_context_required() { + const error = new Error(`server_context_required +Could not resolve \`render\` context. +https://svelte.dev/e/server_context_required`); + error.name = "Svelte error"; + throw error; +} +var ssr_context = null; +function set_ssr_context(v) { + ssr_context = v; +} +function getContext(key) { + const context_map = get_or_init_context_map(); + const result = ( + /** @type {T} */ + context_map.get(key) + ); + return result; +} +function setContext(key, context) { + get_or_init_context_map().set(key, context); + return context; +} +function get_or_init_context_map(name) { + if (ssr_context === null) { + lifecycle_outside_component(); + } + return ssr_context.c ??= new Map(get_parent_context(ssr_context) || void 0); +} +function push(fn) { + ssr_context = { p: ssr_context, c: null, r: null }; +} +function pop() { + ssr_context = /** @type {SSRContext} */ + ssr_context.p; +} +function get_parent_context(ssr_context2) { + let parent = ssr_context2.p; + while (parent !== null) { + const context_map = parent.c; + if (context_map !== null) { + return context_map; + } + parent = parent.p; + } + return null; +} +function unresolved_hydratable(key, stack) { + { + console.warn(`https://svelte.dev/e/unresolved_hydratable`); + } +} +function get_render_context() { + const store = als?.getStore(); + { + server_context_required(); + } + return store; +} +let als = null; +let text_encoder; +let crypto; +const obfuscated_import = (module_name) => import( + /* @vite-ignore */ + module_name +); +async function sha256(data) { + text_encoder ??= new TextEncoder(); + crypto ??= globalThis.crypto?.subtle?.digest ? globalThis.crypto : ( + // @ts-ignore - we don't install node types in the prod build + // don't use import('node:crypto') directly because static analysers will think we rely on node when we don't + (await obfuscated_import("node:crypto")).webcrypto + ); + const hash_buffer = await crypto.subtle.digest("SHA-256", text_encoder.encode(data)); + return base64_encode(hash_buffer); +} +function base64_encode(bytes) { + if (globalThis.Buffer) { + return globalThis.Buffer.from(bytes).toString("base64"); + } + let binary = ""; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} +class Renderer { + /** + * The contents of the renderer. + * @type {RendererItem[]} + */ + #out = []; + /** + * Any `onDestroy` callbacks registered during execution of this renderer. + * @type {(() => void)[] | undefined} + */ + #on_destroy = void 0; + /** + * Whether this renderer is a component body. + * @type {boolean} + */ + #is_component_body = false; + /** + * If set, this renderer is an error boundary. When async collection + * of the children fails, the failed snippet is rendered instead. + * @type {{ + * failed: (renderer: Renderer, error: unknown, reset: () => void) => void; + * transformError: (error: unknown) => unknown; + * context: SSRContext | null; + * } | null} + */ + #boundary = null; + /** + * The type of string content that this renderer is accumulating. + * @type {RendererType} + */ + type; + /** @type {Renderer | undefined} */ + #parent; + /** + * Asynchronous work associated with this renderer + * @type {Promise | undefined} + */ + promise = void 0; + /** + * State which is associated with the content tree as a whole. + * It will be re-exposed, uncopied, on all children. + * @type {SSRState} + * @readonly + */ + global; + /** + * State that is local to the branch it is declared in. + * It will be shallow-copied to all children. + * + * @type {{ select_value: string | undefined }} + */ + local; + /** + * @param {SSRState} global + * @param {Renderer | undefined} [parent] + */ + constructor(global, parent) { + this.#parent = parent; + this.global = global; + this.local = parent ? { ...parent.local } : { select_value: void 0 }; + this.type = parent ? parent.type : "body"; + } + /** + * @param {(renderer: Renderer) => void} fn + */ + head(fn) { + const head = new Renderer(this.global, this); + head.type = "head"; + this.#out.push(head); + head.child(fn); + } + /** + * @param {Array>} blockers + * @param {(renderer: Renderer) => void} fn + */ + async_block(blockers, fn) { + this.#out.push(BLOCK_OPEN); + this.async(blockers, fn); + this.#out.push(BLOCK_CLOSE); + } + /** + * @param {Array>} blockers + * @param {(renderer: Renderer) => void} fn + */ + async(blockers, fn) { + let callback = fn; + if (blockers.length > 0) { + const context = ssr_context; + callback = (renderer) => { + return Promise.all(blockers).then(() => { + const previous_context = ssr_context; + try { + set_ssr_context(context); + return fn(renderer); + } finally { + set_ssr_context(previous_context); + } + }); + }; + } + this.child(callback); + } + /** + * @param {Array<() => void>} thunks + */ + run(thunks) { + const context = ssr_context; + let promise = Promise.resolve(thunks[0]()); + const promises = [promise]; + for (const fn of thunks.slice(1)) { + promise = promise.then(() => { + const previous_context = ssr_context; + set_ssr_context(context); + try { + return fn(); + } finally { + set_ssr_context(previous_context); + } + }); + promises.push(promise); + } + promise.catch(noop); + this.promise = promise; + return promises; + } + /** + * @param {(renderer: Renderer) => MaybePromise} fn + */ + child_block(fn) { + this.#out.push(BLOCK_OPEN); + this.child(fn); + this.#out.push(BLOCK_CLOSE); + } + /** + * Create a child renderer. The child renderer inherits the state from the parent, + * but has its own content. + * @param {(renderer: Renderer) => MaybePromise} fn + */ + child(fn) { + const child = new Renderer(this.global, this); + this.#out.push(child); + const parent = ssr_context; + set_ssr_context({ + ...ssr_context, + p: parent, + c: null, + r: child + }); + const result = fn(child); + set_ssr_context(parent); + if (result instanceof Promise) { + result.catch(noop); + result.finally(() => set_ssr_context(null)).catch(noop); + if (child.global.mode === "sync") { + await_invalid(); + } + child.promise = result; + } + return child; + } + /** + * Render children inside an error boundary. If the children throw and the API-level + * `transformError` transform handles the error (doesn't re-throw), the `failed` snippet is + * rendered instead. Otherwise the error propagates. + * + * @param {{ failed?: (renderer: Renderer, error: unknown, reset: () => void) => void }} props + * @param {(renderer: Renderer) => MaybePromise} children_fn + */ + boundary(props, children_fn) { + const child = new Renderer(this.global, this); + this.#out.push(child); + const parent_context = ssr_context; + if (props.failed) { + child.#boundary = { + failed: props.failed, + transformError: this.global.transformError, + context: parent_context + }; + } + set_ssr_context({ + ...ssr_context, + p: parent_context, + c: null, + r: child + }); + try { + const result = children_fn(child); + set_ssr_context(parent_context); + if (result instanceof Promise) { + if (child.global.mode === "sync") { + await_invalid(); + } + result.catch(noop); + child.promise = result; + } + } catch (error) { + set_ssr_context(parent_context); + const failed_snippet = props.failed; + if (!failed_snippet) throw error; + const result = this.global.transformError(error); + child.#out.length = 0; + child.#boundary = null; + if (result instanceof Promise) { + if (this.global.mode === "sync") { + await_invalid(); + } + child.promise = /** @type {Promise} */ + result.then((transformed) => { + set_ssr_context(parent_context); + child.#out.push(Renderer.#serialize_failed_boundary(transformed)); + failed_snippet(child, transformed, noop); + child.#out.push(BLOCK_CLOSE); + }); + child.promise.catch(noop); + } else { + child.#out.push(Renderer.#serialize_failed_boundary(result)); + failed_snippet(child, result, noop); + child.#out.push(BLOCK_CLOSE); + } + } + } + /** + * Create a component renderer. The component renderer inherits the state from the parent, + * but has its own content. It is treated as an ordering boundary for ondestroy callbacks. + * @param {(renderer: Renderer) => MaybePromise} fn + * @param {Function} [component_fn] + * @returns {void} + */ + component(fn, component_fn) { + push(); + const child = this.child(fn); + child.#is_component_body = true; + pop(); + } + /** + * @param {Record} attrs + * @param {(renderer: Renderer) => void} fn + * @param {string | undefined} [css_hash] + * @param {Record | undefined} [classes] + * @param {Record | undefined} [styles] + * @param {number | undefined} [flags] + * @param {boolean | undefined} [is_rich] + * @returns {void} + */ + select(attrs, fn, css_hash, classes, styles, flags, is_rich) { + const { value, ...select_attrs } = attrs; + this.push(``); + this.child((renderer) => { + renderer.local.select_value = value; + fn(renderer); + }); + this.push(`${is_rich ? "" : ""}`); + } + /** + * @param {Record} attrs + * @param {string | number | boolean | ((renderer: Renderer) => void)} body + * @param {string | undefined} [css_hash] + * @param {Record | undefined} [classes] + * @param {Record | undefined} [styles] + * @param {number | undefined} [flags] + * @param {boolean | undefined} [is_rich] + */ + option(attrs, body, css_hash, classes, styles, flags, is_rich) { + this.#out.push(` { + if (has_own_property.call(attrs, "value")) { + value = attrs.value; + } + if (value === this.local.select_value) { + renderer.#out.push(' selected=""'); + } + renderer.#out.push(`>${body2}${is_rich ? "" : ""}`); + if (head) { + renderer.head((child) => child.push(head)); + } + }; + if (typeof body === "function") { + this.child((renderer) => { + const r = new Renderer(this.global, this); + body(r); + if (this.global.mode === "async") { + return r.#collect_content_async().then((content) => { + close(renderer, content.body.replaceAll("", ""), content); + }); + } else { + const content = r.#collect_content(); + close(renderer, content.body.replaceAll("", ""), content); + } + }); + } else { + close(this, body, { body: escape_html(body) }); + } + } + /** + * @param {(renderer: Renderer) => void} fn + */ + title(fn) { + const path = this.get_path(); + const close = (head) => { + this.global.set_title(head, path); + }; + this.child((renderer) => { + const r = new Renderer(renderer.global, renderer); + fn(r); + if (renderer.global.mode === "async") { + return r.#collect_content_async().then((content) => { + close(content.head); + }); + } else { + const content = r.#collect_content(); + close(content.head); + } + }); + } + /** + * @param {string | (() => Promise)} content + */ + push(content) { + if (typeof content === "function") { + this.child(async (renderer) => renderer.push(await content())); + } else { + this.#out.push(content); + } + } + /** + * @param {() => void} fn + */ + on_destroy(fn) { + (this.#on_destroy ??= []).push(fn); + } + /** + * @returns {number[]} + */ + get_path() { + return this.#parent ? [...this.#parent.get_path(), this.#parent.#out.indexOf(this)] : []; + } + /** + * @deprecated this is needed for legacy component bindings + */ + copy() { + const copy = new Renderer(this.global, this.#parent); + copy.#out = this.#out.map((item) => item instanceof Renderer ? item.copy() : item); + copy.promise = this.promise; + return copy; + } + /** + * @param {Renderer} other + * @deprecated this is needed for legacy component bindings + */ + subsume(other) { + if (this.global.mode !== other.global.mode) { + throw new Error( + "invariant: A renderer cannot switch modes. If you're seeing this, there's a compiler bug. File an issue!" + ); + } + this.local = other.local; + this.#out = other.#out.map((item) => { + if (item instanceof Renderer) { + item.subsume(item); + } + return item; + }); + this.promise = other.promise; + this.type = other.type; + } + get length() { + return this.#out.length; + } + /** + * Creates the hydration comment that marks the start of a failed boundary. + * The error is JSON-serialized and embedded inside an HTML comment for the client + * to parse during hydration. The JSON is escaped to prevent `-->` or ``; + } + /** + * Only available on the server and when compiling with the `server` option. + * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. + * @template {Record} Props + * @param {Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp }} [options] + * @returns {RenderOutput} + */ + static render(component, options = {}) { + let sync; + const result = ( + /** @type {RenderOutput} */ + {} + ); + Object.defineProperties(result, { + html: { + get: () => { + return (sync ??= Renderer.#render(component, options)).body; + } + }, + head: { + get: () => { + return (sync ??= Renderer.#render(component, options)).head; + } + }, + body: { + get: () => { + return (sync ??= Renderer.#render(component, options)).body; + } + }, + hashes: { + value: { + script: "" + } + }, + then: { + value: ( + /** + * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function. + * + * @template TResult1 + * @template [TResult2=never] + * @param { (value: SyncRenderOutput) => TResult1 } onfulfilled + * @param { (reason: unknown) => TResult2 } onrejected + */ + (onfulfilled, onrejected) => { + { + const result2 = sync ??= Renderer.#render(component, options); + const user_result = onfulfilled({ + head: result2.head, + body: result2.body, + html: result2.body, + hashes: { script: [] } + }); + return Promise.resolve(user_result); + } + } + ) + } + }); + return result; + } + /** + * Collect all of the `onDestroy` callbacks registered during rendering. In an async context, this is only safe to call + * after awaiting `collect_async`. + * + * Child renderers are "porous" and don't affect execution order, but component body renderers + * create ordering boundaries. Within a renderer, callbacks run in order until hitting a component boundary. + * @returns {Iterable<() => void>} + */ + *#collect_on_destroy() { + for (const component of this.#traverse_components()) { + yield* component.#collect_ondestroy(); + } + } + /** + * Performs a depth-first search of renderers, yielding the deepest components first, then additional components as we backtrack up the tree. + * @returns {Iterable} + */ + *#traverse_components() { + for (const child of this.#out) { + if (typeof child !== "string") { + yield* child.#traverse_components(); + } + } + if (this.#is_component_body) { + yield this; + } + } + /** + * @returns {Iterable<() => void>} + */ + *#collect_ondestroy() { + if (this.#on_destroy) { + for (const fn of this.#on_destroy) { + yield fn; + } + } + for (const child of this.#out) { + if (child instanceof Renderer && !child.#is_component_body) { + yield* child.#collect_ondestroy(); + } + } + } + /** + * Render a component. Throws if any of the children are performing asynchronous work. + * + * @template {Record} Props + * @param {Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string }} options + * @returns {AccumulatedContent} + */ + static #render(component, options) { + var previous_context = ssr_context; + try { + const renderer = Renderer.#open_render("sync", component, options); + const content = renderer.#collect_content(); + return Renderer.#close_render(content, renderer); + } finally { + abort(); + set_ssr_context(previous_context); + } + } + /** + * Render a component. + * + * @template {Record} Props + * @param {Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp }} options + * @returns {Promise} + */ + static async #render_async(component, options) { + const previous_context = ssr_context; + try { + const renderer = Renderer.#open_render("async", component, options); + const content = await renderer.#collect_content_async(); + const hydratables = await renderer.#collect_hydratables(); + if (hydratables !== null) { + content.head = hydratables + content.head; + } + return Renderer.#close_render(content, renderer); + } finally { + set_ssr_context(previous_context); + abort(); + } + } + /** + * Collect all of the code from the `out` array and return it as a string, or a promise resolving to a string. + * @param {AccumulatedContent} content + * @returns {AccumulatedContent} + */ + #collect_content(content = { head: "", body: "" }) { + for (const item of this.#out) { + if (typeof item === "string") { + content[this.type] += item; + } else if (item instanceof Renderer) { + item.#collect_content(content); + } + } + return content; + } + /** + * Collect all of the code from the `out` array and return it as a string. + * @param {AccumulatedContent} content + * @returns {Promise} + */ + async #collect_content_async(content = { head: "", body: "" }) { + await this.promise; + for (const item of this.#out) { + if (typeof item === "string") { + content[this.type] += item; + } else if (item instanceof Renderer) { + if (item.#boundary) { + const boundary_content = { head: "", body: "" }; + try { + await item.#collect_content_async(boundary_content); + content.head += boundary_content.head; + content.body += boundary_content.body; + } catch (error) { + const { context, failed, transformError } = item.#boundary; + set_ssr_context(context); + let transformed = await transformError(error); + const failed_renderer = new Renderer(item.global, item); + failed_renderer.type = item.type; + failed_renderer.#out.push(Renderer.#serialize_failed_boundary(transformed)); + failed(failed_renderer, transformed, noop); + failed_renderer.#out.push(BLOCK_CLOSE); + await failed_renderer.#collect_content_async(content); + } + } else { + await item.#collect_content_async(content); + } + } + } + return content; + } + async #collect_hydratables() { + const ctx = get_render_context().hydratable; + for (const [_, key] of ctx.unresolved_promises) { + unresolved_hydratable(key, ctx.lookup.get(key)?.stack ?? ""); + } + for (const comparison of ctx.comparisons) { + await comparison; + } + return await this.#hydratable_block(ctx); + } + /** + * @template {Record} Props + * @param {'sync' | 'async'} mode + * @param {import('svelte').Component} component + * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options + * @returns {Renderer} + */ + static #open_render(mode, component, options) { + var previous_context = ssr_context; + try { + const renderer = new Renderer( + new SSRState( + mode, + options.idPrefix ? options.idPrefix + "-" : "", + options.csp, + options.transformError + ) + ); + const context = { p: null, c: options.context ?? null, r: renderer }; + set_ssr_context(context); + renderer.push(BLOCK_OPEN); + component(renderer, options.props ?? {}); + renderer.push(BLOCK_CLOSE); + return renderer; + } finally { + set_ssr_context(previous_context); + } + } + /** + * @param {AccumulatedContent} content + * @param {Renderer} renderer + * @returns {AccumulatedContent & { hashes: { script: Sha256Source[] } }} + */ + static #close_render(content, renderer) { + for (const cleanup of renderer.#collect_on_destroy()) { + cleanup(); + } + let head = content.head + renderer.global.get_title(); + let body = content.body; + for (const { hash, code } of renderer.global.css) { + head += ``; + } + return { + head, + body, + hashes: { + script: renderer.global.csp.script_hashes + } + }; + } + /** + * @param {HydratableContext} ctx + */ + async #hydratable_block(ctx) { + if (ctx.lookup.size === 0) { + return null; + } + let entries = []; + let has_promises = false; + for (const [k, v] of ctx.lookup) { + if (v.promises) { + has_promises = true; + for (const p of v.promises) await p; + } + entries.push(`[${devalue.uneval(k)},${v.serialized}]`); + } + let prelude = `const h = (window.__svelte ??= {}).h ??= new Map();`; + if (has_promises) { + prelude = `const r = (v) => Promise.resolve(v); + ${prelude}`; + } + const body = ` + { + ${prelude} + + for (const [k, v] of [ + ${entries.join(",\n ")} + ]) { + h.set(k, v); + } + } + `; + let csp_attr = ""; + if (this.global.csp.nonce) { + csp_attr = ` nonce="${this.global.csp.nonce}"`; + } else if (this.global.csp.hash) { + const hash = await sha256(body); + this.global.csp.script_hashes.push(`sha256-${hash}`); + } + return ` + ${body}<\/script>`; + } +} +class SSRState { + /** @readonly @type {Csp & { script_hashes: Sha256Source[] }} */ + csp; + /** @readonly @type {'sync' | 'async'} */ + mode; + /** @readonly @type {() => string} */ + uid; + /** @readonly @type {Set<{ hash: string; code: string }>} */ + css = /* @__PURE__ */ new Set(); + /** + * `transformError` passed to `render`. Called when an error boundary catches an error. + * Throws by default if unset in `render`. + * @type {(error: unknown) => unknown} + */ + transformError; + /** @type {{ path: number[], value: string }} */ + #title = { path: [], value: "" }; + /** + * @param {'sync' | 'async'} mode + * @param {string} id_prefix + * @param {Csp} csp + * @param {((error: unknown) => unknown) | undefined} [transformError] + */ + constructor(mode, id_prefix = "", csp = { hash: false }, transformError) { + this.mode = mode; + this.csp = { ...csp, script_hashes: [] }; + this.transformError = transformError ?? ((error) => { + throw error; + }); + let uid = 1; + this.uid = () => `${id_prefix}s${uid++}`; + } + get_title() { + return this.#title.value; + } + /** + * Performs a depth-first (lexicographic) comparison using the path. Rejects sets + * from earlier than or equal to the current value. + * @param {string} value + * @param {number[]} path + */ + set_title(value, path) { + const current = this.#title.path; + let i = 0; + let l = Math.min(path.length, current.length); + while (i < l && path[i] === current[i]) i += 1; + if (path[i] === void 0) return; + if (current[i] === void 0 || path[i] > current[i]) { + this.#title.path = path; + this.#title.value = value; + } + } +} +const INVALID_ATTR_NAME_CHAR_REGEX = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; +function render(component, options = {}) { + if (options.csp?.hash && options.csp.nonce) { + invalid_csp(); + } + return Renderer.render( + /** @type {Component} */ + component, + options + ); +} +function attributes(attrs, css_hash, classes, styles, flags = 0) { + if (styles) { + attrs.style = to_style(attrs.style, styles); + } + if (attrs.class) { + attrs.class = clsx(attrs.class); + } + if (css_hash || classes) { + attrs.class = to_class(attrs.class, css_hash, classes); + } + let attr_str = ""; + let name; + const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0; + const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0; + const is_input = (flags & ELEMENT_IS_INPUT) !== 0; + for (name of Object.keys(attrs)) { + if (typeof attrs[name] === "function") continue; + if (name[0] === "$" && name[1] === "$") continue; + if (INVALID_ATTR_NAME_CHAR_REGEX.test(name)) continue; + var value = attrs[name]; + if (lowercase) { + name = name.toLowerCase(); + } + if (name.length > 2 && name.startsWith("on")) continue; + if (is_input) { + if (name === "defaultvalue" || name === "defaultchecked") { + name = name === "defaultvalue" ? "value" : "checked"; + if (attrs[name]) continue; + } + } + attr_str += attr(name, value, is_html && is_boolean_attribute(name)); + } + return attr_str; +} +function stringify(value) { + return typeof value === "string" ? value : value == null ? "" : value + ""; +} +function attr_class(value, hash, directives) { + var result = to_class(value, hash, directives); + return result ? ` class="${escape_html(result, true)}"` : ""; +} +function ensure_array_like(array_like_or_iterator) { + if (array_like_or_iterator) { + return array_like_or_iterator.length !== void 0 ? array_like_or_iterator : Array.from(array_like_or_iterator); + } + return []; +} +function once(get_value) { + let value = ( + /** @type {V} */ + UNINITIALIZED + ); + return () => { + if (value === UNINITIALIZED) { + value = get_value(); + } + return value; + }; +} +function derived(fn) { + const get_value = ssr_context === null ? fn : once(fn); + let updated_value; + return function(new_value) { + if (arguments.length === 0) { + return updated_value ?? get_value(); + } + updated_value = new_value; + return updated_value; + }; +} +export { + is_passive_event as $, + ASYNC as A, + BOUNDARY_EFFECT as B, + COMMENT_NODE as C, + DIRTY as D, + ERROR_VALUE as E, + EFFECT_TRANSPARENT as F, + EFFECT_PRESERVED as G, + HYDRATION_ERROR as H, + INERT as I, + EAGER_EFFECT as J, + STATE_SYMBOL as K, + object_prototype as L, + MAYBE_DIRTY as M, + array_prototype as N, + get_descriptor as O, + get_prototype_of as P, + is_array as Q, + REACTION_RAN as R, + STALE_REACTION as S, + is_extensible as T, + UNINITIALIZED as U, + USER_EFFECT as V, + WAS_MARKED as W, + REACTION_IS_UPDATING as X, + index_of as Y, + define_property as Z, + array_from as _, + ensure_array_like as a, + LEGACY_PROPS as a0, + render as a1, + setContext as a2, + attr as b, + attr_class as c, + derived as d, + escape_html as e, + HYDRATION_END as f, + getContext as g, + HYDRATION_START as h, + HYDRATION_START_ELSE as i, + EFFECT as j, + CONNECTED as k, + CLEAN as l, + DERIVED as m, + noop as n, + BLOCK_EFFECT as o, + deferred as p, + BRANCH_EFFECT as q, + run_all as r, + stringify as s, + ROOT_EFFECT as t, + RENDER_EFFECT as u, + MANAGED_EFFECT as v, + HEAD_EFFECT as w, + DESTROYED as x, + includes as y, + HYDRATION_START_FAILED as z +}; diff --git a/.svelte-kit/output/server/chunks/index4.js b/.svelte-kit/output/server/chunks/index4.js new file mode 100644 index 0000000..2ae44a6 --- /dev/null +++ b/.svelte-kit/output/server/chunks/index4.js @@ -0,0 +1,47 @@ +import "./state.svelte.js"; +import "clsx"; +import "@sveltejs/kit/internal"; +import "./url.js"; +import "./utils.js"; +import { w as writable } from "./index.js"; +import "@sveltejs/kit/internal/server"; +import "./root.js"; +import "./exports.js"; +import { g as getContext } from "./index3.js"; +function create_updated_store() { + const { set, subscribe } = writable(false); + { + return { + subscribe, + // eslint-disable-next-line @typescript-eslint/require-await + check: async () => false + }; + } +} +const stores = { + updated: /* @__PURE__ */ create_updated_store() +}; +({ + check: stores.updated.check +}); +function context() { + return getContext("__request__"); +} +const page$1 = { + get data() { + return context().page.data; + }, + get error() { + return context().page.error; + }, + get status() { + return context().page.status; + }, + get url() { + return context().page.url; + } +}; +const page = page$1; +export { + page as p +}; diff --git a/.svelte-kit/output/server/chunks/index5.js b/.svelte-kit/output/server/chunks/index5.js new file mode 100644 index 0000000..5a5bcdc --- /dev/null +++ b/.svelte-kit/output/server/chunks/index5.js @@ -0,0 +1,224 @@ +import { logger } from "@better-auth/core/env"; +import { createAdapterFactory } from "@better-auth/core/db/adapter"; +const memoryAdapter = (db, config) => { + let lazyOptions = null; + const adapterCreator = createAdapterFactory({ + config: { + adapterId: "memory", + adapterName: "Memory Adapter", + usePlural: false, + debugLogs: config?.debugLogs || false, + supportsArrays: true, + customTransformInput(props) { + if ((props.options.advanced?.database?.useNumberId || props.options.advanced?.database?.generateId === "serial") && props.field === "id" && props.action === "create") return db[props.model].length + 1; + return props.data; + }, + transaction: async (cb) => { + const clone = structuredClone(db); + try { + return await cb(adapterCreator(lazyOptions)); + } catch (error) { + Object.keys(db).forEach((key) => { + db[key] = clone[key]; + }); + throw error; + } + } + }, + adapter: ({ getFieldName, getDefaultFieldName, options, getModelName }) => { + const applySortToRecords = (records, sortBy, model) => { + if (!sortBy) return records; + return records.sort((a, b) => { + const field = getFieldName({ + model, + field: sortBy.field + }); + const aValue = a[field]; + const bValue = b[field]; + let comparison = 0; + if (aValue == null && bValue == null) comparison = 0; + else if (aValue == null) comparison = -1; + else if (bValue == null) comparison = 1; + else if (typeof aValue === "string" && typeof bValue === "string") comparison = aValue.localeCompare(bValue); + else if (aValue instanceof Date && bValue instanceof Date) comparison = aValue.getTime() - bValue.getTime(); + else if (typeof aValue === "number" && typeof bValue === "number") comparison = aValue - bValue; + else if (typeof aValue === "boolean" && typeof bValue === "boolean") comparison = aValue === bValue ? 0 : aValue ? 1 : -1; + else comparison = String(aValue).localeCompare(String(bValue)); + return sortBy.direction === "asc" ? comparison : -comparison; + }); + }; + function convertWhereClause(where, model, join, select) { + const baseRecords = (() => { + const table = db[model]; + if (!table) { + logger.error(`[MemoryAdapter] Model ${model} not found in the DB`, Object.keys(db)); + throw new Error(`Model ${model} not found`); + } + const evalClause = (record, clause) => { + const { field, value, operator } = clause; + switch (operator) { + case "in": + if (!Array.isArray(value)) throw new Error("Value must be an array"); + return value.includes(record[field]); + case "not_in": + if (!Array.isArray(value)) throw new Error("Value must be an array"); + return !value.includes(record[field]); + case "contains": + return record[field].includes(value); + case "starts_with": + return record[field].startsWith(value); + case "ends_with": + return record[field].endsWith(value); + case "ne": + return record[field] !== value; + case "gt": + return value != null && Boolean(record[field] > value); + case "gte": + return value != null && Boolean(record[field] >= value); + case "lt": + return value != null && Boolean(record[field] < value); + case "lte": + return value != null && Boolean(record[field] <= value); + default: + return record[field] === value; + } + }; + let records = table.filter((record) => { + if (!where.length || where.length === 0) return true; + let result = evalClause(record, where[0]); + for (const clause of where) { + const clauseResult = evalClause(record, clause); + if (clause.connector === "OR") result = result || clauseResult; + else result = result && clauseResult; + } + return result; + }); + if (select?.length && select.length > 0) records = records.map((record) => Object.fromEntries(Object.entries(record).filter(([key]) => select.includes(getDefaultFieldName({ + model, + field: key + }))))); + return records; + })(); + if (!join) return baseRecords; + const grouped = /* @__PURE__ */ new Map(); + const seenIds = /* @__PURE__ */ new Map(); + for (const baseRecord of baseRecords) { + const baseId = String(baseRecord.id); + if (!grouped.has(baseId)) { + const nested = { ...baseRecord }; + for (const [joinModel, joinAttr] of Object.entries(join)) { + const joinModelName = getModelName(joinModel); + if (joinAttr.relation === "one-to-one") nested[joinModelName] = null; + else { + nested[joinModelName] = []; + seenIds.set(`${baseId}-${joinModel}`, /* @__PURE__ */ new Set()); + } + } + grouped.set(baseId, nested); + } + const nestedEntry = grouped.get(baseId); + for (const [joinModel, joinAttr] of Object.entries(join)) { + const joinModelName = getModelName(joinModel); + const joinTable = db[joinModelName]; + if (!joinTable) { + logger.error(`[MemoryAdapter] JoinOption model ${joinModelName} not found in the DB`, Object.keys(db)); + throw new Error(`JoinOption model ${joinModelName} not found`); + } + const matchingRecords = joinTable.filter((joinRecord) => joinRecord[joinAttr.on.to] === baseRecord[joinAttr.on.from]); + if (joinAttr.relation === "one-to-one") nestedEntry[joinModelName] = matchingRecords[0] || null; + else { + const seenSet = seenIds.get(`${baseId}-${joinModel}`); + const limit = joinAttr.limit ?? 100; + let count = 0; + for (const matchingRecord of matchingRecords) { + if (count >= limit) break; + if (!seenSet.has(matchingRecord.id)) { + nestedEntry[joinModelName].push(matchingRecord); + seenSet.add(matchingRecord.id); + count++; + } + } + } + } + } + return Array.from(grouped.values()); + } + return { + create: async ({ model, data }) => { + if (options.advanced?.database?.useNumberId || options.advanced?.database?.generateId === "serial") data.id = db[getModelName(model)].length + 1; + if (!db[model]) db[model] = []; + db[model].push(data); + return data; + }, + findOne: async ({ model, where, select, join }) => { + const res = convertWhereClause(where, model, join, select); + if (join) { + const resArray = res; + if (!resArray.length) return null; + return resArray[0]; + } + return res[0] || null; + }, + findMany: async ({ model, where, sortBy, limit, select, offset, join }) => { + const res = convertWhereClause(where || [], model, join, select); + if (join) { + const resArray = res; + if (!resArray.length) return []; + applySortToRecords(resArray, sortBy, model); + let paginatedRecords = resArray; + if (offset !== void 0) paginatedRecords = paginatedRecords.slice(offset); + if (limit !== void 0) paginatedRecords = paginatedRecords.slice(0, limit); + return paginatedRecords; + } + let table = applySortToRecords(res, sortBy, model); + if (offset !== void 0) table = table.slice(offset); + if (limit !== void 0) table = table.slice(0, limit); + return table || []; + }, + count: async ({ model, where }) => { + if (where) return convertWhereClause(where, model).length; + return db[model].length; + }, + update: async ({ model, where, update }) => { + const res = convertWhereClause(where, model); + res.forEach((record) => { + Object.assign(record, update); + }); + return res[0] || null; + }, + delete: async ({ model, where }) => { + const table = db[model]; + const res = convertWhereClause(where, model); + db[model] = table.filter((record) => !res.includes(record)); + }, + deleteMany: async ({ model, where }) => { + const table = db[model]; + const res = convertWhereClause(where, model); + let count = 0; + db[model] = table.filter((record) => { + if (res.includes(record)) { + count++; + return false; + } + return !res.includes(record); + }); + return count; + }, + updateMany({ model, where, update }) { + const res = convertWhereClause(where, model); + res.forEach((record) => { + Object.assign(record, update); + }); + return res[0] || null; + } + }; + } + }); + return (options) => { + lazyOptions = options; + return adapterCreator(options); + }; +}; +export { + memoryAdapter +}; diff --git a/.svelte-kit/output/server/chunks/index6.js b/.svelte-kit/output/server/chunks/index6.js new file mode 100644 index 0000000..f55374c --- /dev/null +++ b/.svelte-kit/output/server/chunks/index6.js @@ -0,0 +1,307 @@ +import { c as createKyselyAdapter } from "./auth.js"; +import { g } from "./auth.js"; +import { sql } from "kysely"; +import { createAdapterFactory } from "@better-auth/core/db/adapter"; +const kyselyAdapter = (db, config) => { + let lazyOptions = null; + const createCustomAdapter = (db$1) => { + return ({ getFieldName, schema, getDefaultFieldName, getDefaultModelName, getFieldAttributes, getModelName }) => { + const selectAllJoins = (join) => { + const allSelects = []; + const allSelectsStr = []; + if (join) for (const [joinModel, _] of Object.entries(join)) { + const fields = schema[getDefaultModelName(joinModel)]?.fields; + const [_joinModelSchema, joinModelName] = joinModel.includes(".") ? joinModel.split(".") : [void 0, joinModel]; + if (!fields) continue; + fields.id = { type: "string" }; + for (const [field, fieldAttr] of Object.entries(fields)) { + allSelects.push(sql`${sql.ref(`join_${joinModelName}`)}.${sql.ref(fieldAttr.fieldName || field)} as ${sql.ref(`_joined_${joinModelName}_${fieldAttr.fieldName || field}`)}`); + allSelectsStr.push({ + joinModel, + joinModelRef: joinModelName, + fieldName: fieldAttr.fieldName || field + }); + } + } + return { + allSelectsStr, + allSelects + }; + }; + const withReturning = async (values, builder, model, where) => { + let res; + if (config?.type === "mysql") { + await builder.execute(); + const field = values.id ? "id" : where.length > 0 && where[0]?.field ? where[0].field : "id"; + if (!values.id && where.length === 0) { + res = await db$1.selectFrom(model).selectAll().orderBy(getFieldName({ + model, + field + }), "desc").limit(1).executeTakeFirst(); + return res; + } + const value = values[field] || where[0]?.value; + res = await db$1.selectFrom(model).selectAll().orderBy(getFieldName({ + model, + field + }), "desc").where(getFieldName({ + model, + field + }), "=", value).limit(1).executeTakeFirst(); + return res; + } + if (config?.type === "mssql") { + res = await builder.outputAll("inserted").executeTakeFirst(); + return res; + } + res = await builder.returningAll().executeTakeFirst(); + return res; + }; + function convertWhereClause(model, w) { + if (!w) return { + and: null, + or: null + }; + const conditions = { + and: [], + or: [] + }; + w.forEach((condition) => { + const { field: _field, value: _value, operator = "=", connector = "AND" } = condition; + const value = _value; + const field = getFieldName({ + model, + field: _field + }); + const expr = (eb) => { + const f = `${model}.${field}`; + if (operator.toLowerCase() === "in") return eb(f, "in", Array.isArray(value) ? value : [value]); + if (operator.toLowerCase() === "not_in") return eb(f, "not in", Array.isArray(value) ? value : [value]); + if (operator === "contains") return eb(f, "like", `%${value}%`); + if (operator === "starts_with") return eb(f, "like", `${value}%`); + if (operator === "ends_with") return eb(f, "like", `%${value}`); + if (operator === "eq") return eb(f, "=", value); + if (operator === "ne") return eb(f, "<>", value); + if (operator === "gt") return eb(f, ">", value); + if (operator === "gte") return eb(f, ">=", value); + if (operator === "lt") return eb(f, "<", value); + if (operator === "lte") return eb(f, "<=", value); + return eb(f, operator, value); + }; + if (connector === "OR") conditions.or.push(expr); + else conditions.and.push(expr); + }); + return { + and: conditions.and.length ? conditions.and : null, + or: conditions.or.length ? conditions.or : null + }; + } + function processJoinedResults(rows, joinConfig, allSelectsStr) { + if (!joinConfig || !rows.length) return rows; + const groupedByMainId = /* @__PURE__ */ new Map(); + for (const currentRow of rows) { + const mainModelFields = {}; + const joinedModelFields = {}; + for (const [joinModel] of Object.entries(joinConfig)) joinedModelFields[getModelName(joinModel)] = {}; + for (const [key, value] of Object.entries(currentRow)) { + const keyStr = String(key); + let assigned = false; + for (const { joinModel, fieldName, joinModelRef } of allSelectsStr) if (keyStr === `_joined_${joinModelRef}_${fieldName}`) { + joinedModelFields[getModelName(joinModel)][getFieldName({ + model: joinModel, + field: fieldName + })] = value; + assigned = true; + break; + } + if (!assigned) mainModelFields[key] = value; + } + const mainId = mainModelFields.id; + if (!mainId) continue; + if (!groupedByMainId.has(mainId)) { + const entry$1 = { ...mainModelFields }; + for (const [joinModel, joinAttr] of Object.entries(joinConfig)) entry$1[getModelName(joinModel)] = joinAttr.relation === "one-to-one" ? null : []; + groupedByMainId.set(mainId, entry$1); + } + const entry = groupedByMainId.get(mainId); + for (const [joinModel, joinAttr] of Object.entries(joinConfig)) { + const isUnique = joinAttr.relation === "one-to-one"; + const limit = joinAttr.limit ?? 100; + const joinedObj = joinedModelFields[getModelName(joinModel)]; + const hasData = joinedObj && Object.keys(joinedObj).length > 0 && Object.values(joinedObj).some((value) => value !== null && value !== void 0); + if (isUnique) entry[getModelName(joinModel)] = hasData ? joinedObj : null; + else { + const joinModelName = getModelName(joinModel); + if (Array.isArray(entry[joinModelName]) && hasData) { + if (entry[joinModelName].length >= limit) continue; + const idFieldName = getFieldName({ + model: joinModel, + field: "id" + }); + const joinedId = joinedObj[idFieldName]; + if (joinedId) { + if (!entry[joinModelName].some((item) => item[idFieldName] === joinedId) && entry[joinModelName].length < limit) entry[joinModelName].push(joinedObj); + } else if (entry[joinModelName].length < limit) entry[joinModelName].push(joinedObj); + } + } + } + } + const result = Array.from(groupedByMainId.values()); + for (const entry of result) for (const [joinModel, joinAttr] of Object.entries(joinConfig)) if (joinAttr.relation !== "one-to-one") { + const joinModelName = getModelName(joinModel); + if (Array.isArray(entry[joinModelName])) { + const limit = joinAttr.limit ?? 100; + if (entry[joinModelName].length > limit) entry[joinModelName] = entry[joinModelName].slice(0, limit); + } + } + return result; + } + return { + async create({ data, model }) { + return await withReturning(data, db$1.insertInto(model).values(data), model, []); + }, + async findOne({ model, where, select, join }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.selectFrom((eb) => { + let b = eb.selectFrom(model); + if (and) b = b.where((eb$1) => eb$1.and(and.map((expr) => expr(eb$1)))); + if (or) b = b.where((eb$1) => eb$1.or(or.map((expr) => expr(eb$1)))); + if (select?.length && select.length > 0) b = b.select(select.map((field) => getFieldName({ + model, + field + }))); + else b = b.selectAll(); + return b.as("primary"); + }).selectAll("primary"); + if (join) for (const [joinModel, joinAttr] of Object.entries(join)) { + const [_joinModelSchema, joinModelName] = joinModel.includes(".") ? joinModel.split(".") : [void 0, joinModel]; + query = query.leftJoin(`${joinModel} as join_${joinModelName}`, (join$1) => join$1.onRef(`join_${joinModelName}.${joinAttr.on.to}`, "=", `primary.${joinAttr.on.from}`)); + } + const { allSelectsStr, allSelects } = selectAllJoins(join); + query = query.select(allSelects); + const res = await query.execute(); + if (!res || !Array.isArray(res) || res.length === 0) return null; + const row = res[0]; + if (join) return processJoinedResults(res, join, allSelectsStr)[0]; + return row; + }, + async findMany({ model, where, limit, select, offset, sortBy, join }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.selectFrom((eb) => { + let b = eb.selectFrom(model); + if (config?.type === "mssql") { + if (offset !== void 0) { + if (!sortBy) b = b.orderBy(getFieldName({ + model, + field: "id" + })); + b = b.offset(offset).fetch(limit || 100); + } else if (limit !== void 0) b = b.top(limit); + } else { + if (limit !== void 0) b = b.limit(limit); + if (offset !== void 0) b = b.offset(offset); + } + if (sortBy?.field) b = b.orderBy(`${getFieldName({ + model, + field: sortBy.field + })}`, sortBy.direction); + if (and) b = b.where((eb$1) => eb$1.and(and.map((expr) => expr(eb$1)))); + if (or) b = b.where((eb$1) => eb$1.or(or.map((expr) => expr(eb$1)))); + if (select?.length && select.length > 0) b = b.select(select.map((field) => getFieldName({ + model, + field + }))); + else b = b.selectAll(); + return b.as("primary"); + }).selectAll("primary"); + if (join) for (const [joinModel, joinAttr] of Object.entries(join)) { + const [_joinModelSchema, joinModelName] = joinModel.includes(".") ? joinModel.split(".") : [void 0, joinModel]; + query = query.leftJoin(`${joinModel} as join_${joinModelName}`, (join$1) => join$1.onRef(`join_${joinModelName}.${joinAttr.on.to}`, "=", `primary.${joinAttr.on.from}`)); + } + const { allSelectsStr, allSelects } = selectAllJoins(join); + query = query.select(allSelects); + if (sortBy?.field) query = query.orderBy(`${getFieldName({ + model, + field: sortBy.field + })}`, sortBy.direction); + const res = await query.execute(); + if (!res) return []; + if (join) return processJoinedResults(res, join, allSelectsStr); + return res; + }, + async update({ model, where, update: values }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.updateTable(model).set(values); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + return await withReturning(values, query, model, where); + }, + async updateMany({ model, where, update: values }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.updateTable(model).set(values); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + const res = (await query.executeTakeFirst()).numUpdatedRows; + return res > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : Number(res); + }, + async count({ model, where }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.selectFrom(model).select(db$1.fn.count("id").as("count")); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + const res = await query.execute(); + if (typeof res[0].count === "number") return res[0].count; + if (typeof res[0].count === "bigint") return Number(res[0].count); + return parseInt(res[0].count); + }, + async delete({ model, where }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.deleteFrom(model); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + await query.execute(); + }, + async deleteMany({ model, where }) { + const { and, or } = convertWhereClause(model, where); + let query = db$1.deleteFrom(model); + if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb)))); + if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb)))); + const res = (await query.executeTakeFirst()).numDeletedRows; + return res > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : Number(res); + }, + options: config + }; + }; + }; + let adapterOptions = null; + adapterOptions = { + config: { + adapterId: "kysely", + adapterName: "Kysely Adapter", + usePlural: config?.usePlural, + debugLogs: config?.debugLogs, + supportsBooleans: config?.type === "sqlite" || config?.type === "mssql" || config?.type === "mysql" || !config?.type ? false : true, + supportsDates: config?.type === "sqlite" || config?.type === "mssql" || !config?.type ? false : true, + supportsJSON: config?.type === "postgres" ? true : false, + supportsArrays: false, + supportsUUIDs: config?.type === "postgres" ? true : false, + transaction: config?.transaction ? (cb) => db.transaction().execute((trx) => { + return cb(createAdapterFactory({ + config: adapterOptions.config, + adapter: createCustomAdapter(trx) + })(lazyOptions)); + }) : false + }, + adapter: createCustomAdapter(db) + }; + const adapter = createAdapterFactory(adapterOptions); + return (options) => { + lazyOptions = options; + return adapter(options); + }; +}; +export { + createKyselyAdapter, + g as getKyselyDatabaseType, + kyselyAdapter +}; diff --git a/.svelte-kit/output/server/chunks/internal.js b/.svelte-kit/output/server/chunks/internal.js new file mode 100644 index 0000000..55b3eeb --- /dev/null +++ b/.svelte-kit/output/server/chunks/internal.js @@ -0,0 +1,127 @@ +import { r as root } from "./root.js"; +import "./environment.js"; +import "./server.js"; +import "./shared-server.js"; +let read_implementation = null; +function set_read_implementation(fn) { + read_implementation = fn; +} +function set_manifest(_) { +} +const options = { + app_template_contains_nonce: false, + async: false, + csp: { "mode": "auto", "directives": { "upgrade-insecure-requests": false, "block-all-mixed-content": false }, "reportOnly": { "upgrade-insecure-requests": false, "block-all-mixed-content": false } }, + csrf_check_origin: true, + csrf_trusted_origins: [], + embedded: false, + env_public_prefix: "PUBLIC_", + env_private_prefix: "", + hash_routing: false, + hooks: null, + // added lazily, via `get_hooks` + preload_strategy: "modulepreload", + root, + service_worker: false, + service_worker_options: void 0, + templates: { + app: ({ head, body, assets, nonce, env }) => '\n\n \n \n \n \n \n \n Vash Esports\n ' + head + '\n \n \n
' + body + "
\n \n\n", + error: ({ status, message }) => '\n\n \n \n ' + message + ` + + + + +
+ ` + status + '\n
\n

' + message + "

\n
\n
\n \n\n" + }, + version_hash: "12t8tv8" +}; +async function get_hooks() { + let handle; + let handleFetch; + let handleError; + let handleValidationError; + let init; + ({ handle, handleFetch, handleError, handleValidationError, init } = await import("../entries/hooks.server.js")); + let reroute; + let transport; + return { + handle, + handleFetch, + handleError, + handleValidationError, + init, + reroute, + transport + }; +} +export { + set_manifest as a, + get_hooks as g, + options as o, + read_implementation as r, + set_read_implementation as s +}; diff --git a/.svelte-kit/output/server/chunks/node-sqlite-dialect.js b/.svelte-kit/output/server/chunks/node-sqlite-dialect.js new file mode 100644 index 0000000..68f03bc --- /dev/null +++ b/.svelte-kit/output/server/chunks/node-sqlite-dialect.js @@ -0,0 +1,155 @@ +import { CompiledQuery, DefaultQueryCompiler, DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE, sql } from "kysely"; +var NodeSqliteAdapter = class { + get supportsCreateIfNotExists() { + return true; + } + get supportsTransactionalDdl() { + return false; + } + get supportsReturning() { + return true; + } + async acquireMigrationLock() { + } + async releaseMigrationLock() { + } + get supportsOutput() { + return true; + } +}; +var NodeSqliteDriver = class { + #config; + #connectionMutex = new ConnectionMutex(); + #db; + #connection; + constructor(config) { + this.#config = { ...config }; + } + async init() { + this.#db = this.#config.database; + this.#connection = new NodeSqliteConnection(this.#db); + if (this.#config.onCreateConnection) await this.#config.onCreateConnection(this.#connection); + } + async acquireConnection() { + await this.#connectionMutex.lock(); + return this.#connection; + } + async beginTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("begin")); + } + async commitTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("commit")); + } + async rollbackTransaction(connection) { + await connection.executeQuery(CompiledQuery.raw("rollback")); + } + async releaseConnection() { + this.#connectionMutex.unlock(); + } + async destroy() { + this.#db?.close(); + } +}; +var NodeSqliteConnection = class { + #db; + constructor(db) { + this.#db = db; + } + executeQuery(compiledQuery) { + const { sql: sql$1, parameters } = compiledQuery; + const rows = this.#db.prepare(sql$1).all(...parameters); + return Promise.resolve({ rows }); + } + async *streamQuery() { + throw new Error("Streaming query is not supported by SQLite driver."); + } +}; +var ConnectionMutex = class { + #promise; + #resolve; + async lock() { + while (await this.#promise) await this.#promise; + this.#promise = new Promise((resolve) => { + this.#resolve = resolve; + }); + } + unlock() { + const resolve = this.#resolve; + this.#promise = void 0; + this.#resolve = void 0; + resolve?.(); + } +}; +var NodeSqliteIntrospector = class { + #db; + constructor(db) { + this.#db = db; + } + async getSchemas() { + return []; + } + async getTables(options = { withInternalKyselyTables: false }) { + let query = this.#db.selectFrom("sqlite_schema").where("type", "=", "table").where("name", "not like", "sqlite_%").select("name").$castTo(); + if (!options.withInternalKyselyTables) query = query.where("name", "!=", DEFAULT_MIGRATION_TABLE).where("name", "!=", DEFAULT_MIGRATION_LOCK_TABLE); + const tables = await query.execute(); + return Promise.all(tables.map(({ name }) => this.#getTableMetadata(name))); + } + async getMetadata(options) { + return { tables: await this.getTables(options) }; + } + async #getTableMetadata(table) { + const db = this.#db; + const autoIncrementCol = (await db.selectFrom("sqlite_master").where("name", "=", table).select("sql").$castTo().execute())[0]?.sql?.split(/[\(\),]/)?.find((it) => it.toLowerCase().includes("autoincrement"))?.split(/\s+/)?.[0]?.replace(/["`]/g, ""); + return { + name: table, + columns: (await db.selectFrom(sql`pragma_table_info(${table})`.as("table_info")).select([ + "name", + "type", + "notnull", + "dflt_value" + ]).execute()).map((col) => ({ + name: col.name, + dataType: col.type, + isNullable: !col.notnull, + isAutoIncrementing: col.name === autoIncrementCol, + hasDefaultValue: col.dflt_value != null + })), + isView: true + }; + } +}; +var NodeSqliteQueryCompiler = class extends DefaultQueryCompiler { + getCurrentParameterPlaceholder() { + return "?"; + } + getLeftIdentifierWrapper() { + return '"'; + } + getRightIdentifierWrapper() { + return '"'; + } + getAutoIncrement() { + return "autoincrement"; + } +}; +var NodeSqliteDialect = class { + #config; + constructor(config) { + this.#config = { ...config }; + } + createDriver() { + return new NodeSqliteDriver(this.#config); + } + createQueryCompiler() { + return new NodeSqliteQueryCompiler(); + } + createAdapter() { + return new NodeSqliteAdapter(); + } + createIntrospector(db) { + return new NodeSqliteIntrospector(db); + } +}; +export { + NodeSqliteDialect +}; diff --git a/.svelte-kit/output/server/chunks/orchestrator.js b/.svelte-kit/output/server/chunks/orchestrator.js new file mode 100644 index 0000000..da259e9 --- /dev/null +++ b/.svelte-kit/output/server/chunks/orchestrator.js @@ -0,0 +1,448 @@ +import { d as db, b as match, u as user, g as matchGame } from "./index2.js"; +import { eq } from "drizzle-orm"; +import { g as getMatchFull, M as MATCH_STATES, s as submitRoll, p as pickMap, d as submitGameScores } from "./engine.js"; +import { b as private_env } from "./shared-server.js"; +let client = null; +let connecting = null; +async function getClient() { + if (client?.isConnected?.()) return client; + if (connecting) { + await connecting; + return client; + } + if (!private_env.OSU_IRC_USERNAME || !private_env.OSU_IRC_PASSWORD) { + throw new Error("OSU_IRC_USERNAME and OSU_IRC_PASSWORD must be set in .env"); + } + const banchoModule = await import("bancho.js"); + const BanchoClient = banchoModule.BanchoClient ?? banchoModule.default?.BanchoClient ?? banchoModule.default; + client = new BanchoClient({ + username: private_env.OSU_IRC_USERNAME, + password: private_env.OSU_IRC_PASSWORD + }); + connecting = client.connect().then(() => { + console.log("[Bancho] Connected as", private_env.OSU_IRC_USERNAME); + connecting = null; + }); + await connecting; + return client; +} +class TournamentLobby { + channel = null; + channelName = ""; + osuMatchId = 0; + // Score collection + collectedScores = []; + matchFinishedResolve = null; + // Ready tracking + allReadyResolve = null; + // Event callbacks (set by orchestrator) + onRollResult = null; + onPickCommand = null; + /** + * Create a new multiplayer lobby via !mp make. + */ + async create(name) { + const c = await getClient(); + return new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(new Error("Lobby creation timed out")), + 15e3 + ); + const banchoBot = c.getUser("BanchoBot"); + const onPM = (msg) => { + const text = msg.message ?? String(msg); + const m = text.match(/https:\/\/osu\.ppy\.sh\/mp\/(\d+)/); + if (m) { + clearTimeout(timeout); + banchoBot.removeListener("message", onPM); + this.osuMatchId = parseInt(m[1]); + this.channelName = `#mp_${this.osuMatchId}`; + this.joinChannel(c).then( + () => resolve({ matchId: this.osuMatchId, channel: this.channelName }) + ); + } + }; + banchoBot.on("message", onPM); + banchoBot.sendMessage(`!mp make ${name}`); + }); + } + async joinChannel(c) { + this.channel = c.getChannel(this.channelName); + await this.channel.join(); + this.channel.on("message", (msg) => { + const text = msg.message ?? String(msg); + const sender = msg.user?.ircUsername ?? ""; + if (sender === "BanchoBot") { + this.parseBanchoMessage(text); + } else if (sender) { + this.parsePlayerMessage(sender, text); + } + }); + } + // ── Message Parsing ───────────────────────────────────────────────── + parseBanchoMessage(text) { + const scoreMatch = text.match( + /^(.+?) finished playing \(Score: ([\d,]+).*?(PASSED|FAILED)\)/i + ); + if (scoreMatch) { + this.collectedScores.push({ + username: scoreMatch[1].trim(), + score: parseInt(scoreMatch[2].replace(/,/g, "")), + passed: scoreMatch[3] === "PASSED" + }); + console.log(`[Bancho] Score: ${scoreMatch[1].trim()} = ${scoreMatch[2]}`); + return; + } + if (text.includes("The match has finished!")) { + console.log("[Bancho] Game finished. Scores:", this.collectedScores); + if (this.matchFinishedResolve) { + this.matchFinishedResolve([...this.collectedScores]); + this.matchFinishedResolve = null; + } + this.collectedScores = []; + return; + } + const rollMatch = text.match(/^(.+?) rolls (\d+) point\(s\)/); + if (rollMatch) { + const username = rollMatch[1].trim(); + const value = parseInt(rollMatch[2]); + console.log(`[Bancho] Roll: ${username} = ${value}`); + if (this.onRollResult) { + this.onRollResult(username, value); + } + return; + } + if (text.includes("All players are ready")) { + console.log("[Bancho] All players ready"); + if (this.allReadyResolve) { + this.allReadyResolve(); + this.allReadyResolve = null; + } + return; + } + } + parsePlayerMessage(sender, text) { + const pickMatch = text.match(/^!pick\s+([A-Z]{2})(\d*)/i); + if (pickMatch) { + const category = pickMatch[1].toUpperCase(); + const num = pickMatch[2] ? parseInt(pickMatch[2]) : 1; + const label = `${category}${num}`; + console.log(`[Bancho] Pick command from ${sender}: ${label}`); + if (this.onPickCommand) { + this.onPickCommand(sender, label); + } + return; + } + } + // ── IRC Commands ──────────────────────────────────────────────────── + async send(cmd) { + if (!this.channel) throw new Error("Lobby not created"); + console.log(`[Bancho] ${this.channelName} > ${cmd}`); + await this.channel.sendMessage(cmd); + } + async invite(username) { + await this.send(`!mp invite ${username.replace(/ /g, "_")}`); + } + async setProperties(teamMode, scoreMode, size) { + await this.send(`!mp set ${teamMode} ${scoreMode} ${size}`); + } + async setMap(beatmapId, playmode = 0) { + await this.send(`!mp map ${beatmapId} ${playmode}`); + } + async setMods(mods) { + const filtered = mods.filter((m) => !["NM", "TB", "None", "NF"].includes(m)); + if (mods.includes("FM") || mods.includes("Freemod")) { + await this.send("!mp mods Freemod NF"); + } else if (filtered.length > 0) { + await this.send(`!mp mods NF ${filtered.join(" ")}`); + } else { + await this.send("!mp mods NF"); + } + } + async startGame(delaySec = 5) { + await this.send(`!mp start ${delaySec}`); + } + async close() { + try { + await this.send("!mp close"); + } catch { + } + } + async chat(message) { + await this.send(message); + } + // ── Async Waiters ─────────────────────────────────────────────────── + /** + * Wait for all players to click "Ready" in osu! client. + * BanchoBot sends "All players are ready" when they do. + */ + waitForReady(timeoutMs = 18e4) { + return new Promise((resolve, reject) => { + const t = setTimeout(() => { + this.allReadyResolve = null; + reject(new Error("Timed out waiting for players to ready")); + }, timeoutMs); + this.allReadyResolve = () => { + clearTimeout(t); + resolve(); + }; + }); + } + /** + * Wait for "The match has finished!" and return all player scores. + */ + waitForScores(timeoutMs = 6e5) { + this.collectedScores = []; + return new Promise((resolve, reject) => { + const t = setTimeout(() => { + this.matchFinishedResolve = null; + reject(new Error("Timed out waiting for match scores")); + }, timeoutMs); + this.matchFinishedResolve = (scores) => { + clearTimeout(t); + resolve(scores); + }; + }); + } +} +const lobbies = /* @__PURE__ */ new Map(); +function getLobby(matchId) { + return lobbies.get(matchId); +} +function setLobby(matchId, lobby) { + lobbies.set(matchId, lobby); +} +function removeLobby(matchId) { + lobbies.delete(matchId); +} +function sleep(ms) { + return new Promise((r) => setTimeout(r, ms)); +} +async function initMatchLobby(matchId) { + const m = await getMatchFull(matchId); + const config = m.config; + const lobby = new TournamentLobby(); + const lobbyName = `VASH: (${m.participants[0]?.team.name}) vs (${m.participants[1]?.team.name})`; + console.log(`[Orchestrator] Creating lobby: ${lobbyName}`); + const { matchId: osuId, channel } = await lobby.create(lobbyName); + console.log(`[Orchestrator] Created ${channel} (osu mp/${osuId})`); + setLobby(matchId, lobby); + await db.update(match).set({ state: MATCH_STATES.LOBBY, osuLobbyId: osuId, startedAt: /* @__PURE__ */ new Date() }).where(eq(match.id, matchId)); + const scoreMode = config.scoringType === "score_v2" ? 3 : config.scoringType === "accuracy" ? 1 : config.scoringType === "combo" ? 2 : 0; + await lobby.setProperties(0, scoreMode, config.teamSize * 2); + await lobby.send("!mp password"); + await sleep(500); + const invited = /* @__PURE__ */ new Set(); + for (const p of m.participants) { + for (const pl of p.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name && !invited.has(u.name.toLowerCase())) { + await lobby.invite(u.name); + invited.add(u.name.toLowerCase()); + console.log(`[Orchestrator] Invited ${u.name}`); + await sleep(300); + } + } + } + setupChatHandlers(matchId, lobby); + await lobby.chat( + `Welcome! BO${config.bestOf}. Type !roll to decide pick order, or use the web UI.` + ); + await db.update(match).set({ state: MATCH_STATES.ROLLING }).where(eq(match.id, matchId)); + return { osuMatchId: osuId, channel }; +} +function setupChatHandlers(matchId, lobby) { + lobby.onRollResult = async (username, value) => { + try { + const m = await getMatchFull(matchId); + if (m.state !== MATCH_STATES.ROLLING) { + await lobby.chat(`Rolls are not active right now.`); + return; + } + let targetParticipant = null; + for (const p of m.participants) { + for (const pl of p.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name?.toLowerCase() === username.toLowerCase().replace(/_/g, " ")) { + if (p.rollValue === null) { + targetParticipant = p; + break; + } else if (!targetParticipant) { + const unrolled = m.participants.find((pp) => pp.rollValue === null); + if (unrolled) targetParticipant = unrolled; + } + } + } + if (targetParticipant?.rollValue === null) break; + } + if (!targetParticipant || targetParticipant.rollValue !== null) { + await lobby.chat(`${username}: already rolled or not in this match.`); + return; + } + await submitRoll(matchId, targetParticipant.id, value); + console.log(`[Orchestrator] IRC roll: ${username} = ${value}`); + const updated = await getMatchFull(matchId); + if (updated.state === MATCH_STATES.PICKING) { + const sorted = [...updated.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + await lobby.chat( + `Rolls complete! ${sorted[0]?.team.name} picks first. Use !pick (e.g. !pick NM1) or pick in web UI.` + ); + } + } catch (err) { + console.error("[Orchestrator] IRC roll error:", err.message); + } + }; + lobby.onPickCommand = async (username, slotLabel) => { + try { + const m = await getMatchFull(matchId); + if (m.state !== MATCH_STATES.PICKING) { + await lobby.chat(`Picks are not active right now.`); + return; + } + const labelMatch = slotLabel.match(/^([A-Z]{2})(\d+)$/); + if (!labelMatch) { + await lobby.chat(`${username}: Invalid slot. Use format like NM1, HD2, DT1.`); + return; + } + const category = labelMatch[1]; + const order = parseInt(labelMatch[2]); + const sorted = [...m.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + const expectedIdx = m.games.length % sorted.length; + const expectedPicker = sorted[expectedIdx]; + let isPickerTurn = false; + if (expectedPicker) { + for (const pl of expectedPicker.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name?.toLowerCase() === username.toLowerCase().replace(/_/g, " ")) { + isPickerTurn = true; + break; + } + } + } + if (!isPickerTurn && m.participants[0]?.teamId === m.participants[1]?.teamId) { + isPickerTurn = true; + } + if (!isPickerTurn) { + await lobby.chat(`${username}: It's not your turn to pick.`); + return; + } + const slot = m.mappool?.slots?.find( + (s) => s.category === category && s.orderInCategory === order + ); + if (!slot) { + await lobby.chat(`${username}: Slot ${slotLabel} not found in this mappool.`); + return; + } + const alreadyPlayed = m.games.some((g) => g.mappoolSlotId === slot.id); + if (alreadyPlayed) { + await lobby.chat(`${username}: ${slotLabel} has already been played.`); + return; + } + const game = await pickMap(matchId, expectedPicker.id, slot.id); + console.log(`[Orchestrator] IRC pick: ${username} picked ${slotLabel}`); + playPickedMap(matchId, game.id).catch( + (err) => console.error("[Orchestrator] IRC play failed:", err.message) + ); + } catch (err) { + console.error("[Orchestrator] IRC pick error:", err.message); + await lobby.chat(`Error: ${err.message}`).catch(() => { + }); + } + }; +} +async function playPickedMap(matchId, matchGameId) { + const lobby = getLobby(matchId); + if (!lobby) { + console.warn("[Orchestrator] No IRC lobby for", matchId, "— skipping IRC"); + return; + } + const game = await db.query.matchGame.findFirst({ + where: eq(matchGame.id, matchGameId), + with: { slot: true } + }); + if (!game) throw new Error("Game not found"); + const slot = game.slot; + await lobby.setMap(slot.beatmapId); + await sleep(1e3); + await lobby.setMods(slot.mods); + await sleep(500); + await lobby.chat( + `Playing ${slot.category}${slot.orderInCategory}. Ready up! (Game starts when all players are ready)` + ); + try { + await lobby.waitForReady(); + await lobby.chat("All ready — starting in 5s!"); + await lobby.startGame(5); + } catch (err) { + console.warn("[Orchestrator] Ready timeout:", err.message); + await lobby.chat("Ready timed out. Use the web UI to force start, or ready up!"); + return; + } + collectScores(matchId, matchGameId, lobby).catch( + (err) => console.error("[Orchestrator] Score collection failed:", err) + ); +} +async function forceStartGame(matchId) { + const lobby = getLobby(matchId); + if (!lobby) return; + await lobby.chat("Force starting in 10s!"); + await lobby.startGame(10); +} +async function collectScores(matchId, matchGameId, lobby) { + const ircScores = await lobby.waitForScores(); + console.log(`[Orchestrator] Scores for game ${matchGameId}:`, ircScores); + const m = await getMatchFull(matchId); + const scores = []; + for (const irc of ircScores) { + for (const participant of m.participants) { + for (const player of participant.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, player.userId) }); + const ircName = irc.username.toLowerCase().replace(/_/g, " "); + const dbName = u?.name?.toLowerCase(); + if (dbName && (dbName === ircName || dbName === irc.username.toLowerCase())) { + scores.push({ playerId: player.id, score: irc.score, passed: irc.passed }); + } + } + } + } + if (scores.length === 0) { + console.warn("[Orchestrator] No scores matched any IRC usernames"); + return; + } + await submitGameScores(matchGameId, scores); + const updated = await getMatchFull(matchId); + if (updated.state === MATCH_STATES.FINISHED) { + const winner = updated.participants.find((p) => p.teamId === updated.winnerId); + await lobby.chat(`GG! ${winner?.team.name ?? "?"} wins the match!`); + await sleep(5e3); + await lobby.close(); + removeLobby(matchId); + } else { + const sorted = [...updated.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + const nextIdx = updated.games.length % sorted.length; + const nextPicker = sorted[nextIdx]; + await lobby.chat( + `${nextPicker?.team.name}'s turn to pick. Use !pick (e.g. !pick HD1) or pick in web UI.` + ); + } +} +async function closeLobby(matchId) { + const lobby = getLobby(matchId); + if (lobby) { + await lobby.close(); + removeLobby(matchId); + } +} +export { + closeLobby as c, + forceStartGame as f, + getLobby as g, + initMatchLobby as i, + playPickedMap as p +}; diff --git a/.svelte-kit/output/server/chunks/query.js b/.svelte-kit/output/server/chunks/query.js new file mode 100644 index 0000000..a14a6fa --- /dev/null +++ b/.svelte-kit/output/server/chunks/query.js @@ -0,0 +1,258 @@ +import { get_request_store, with_request_store } from "@sveltejs/kit/internal/server"; +import { z as stringify_remote_arg, f as create_remote_key, h as handle_error_and_jsonify } from "./shared.js"; +import { p as prerendering } from "./environment.js"; +import { parse } from "devalue"; +import { error } from "@sveltejs/kit"; +import { HttpError, SvelteKitError } from "@sveltejs/kit/internal"; +function create_validator(validate_or_fn, maybe_fn) { + if (!maybe_fn) { + return (arg) => { + if (arg !== void 0) { + error(400, "Bad Request"); + } + }; + } + if (validate_or_fn === "unchecked") { + return (arg) => arg; + } + if ("~standard" in validate_or_fn) { + return async (arg) => { + const { event, state } = get_request_store(); + const result = await validate_or_fn["~standard"].validate(arg); + if (result.issues) { + error( + 400, + await state.handleValidationError({ + issues: result.issues, + event + }) + ); + } + return result.value; + }; + } + throw new Error( + 'Invalid validator passed to remote function. Expected "unchecked" or a Standard Schema (https://standardschema.dev)' + ); +} +async function get_response(info, arg, state, get_result) { + await 0; + const cache = get_cache(info, state); + return cache[stringify_remote_arg(arg, state.transport)] ??= get_result(); +} +function parse_remote_response(data, transport) { + const revivers = {}; + for (const key in transport) { + revivers[key] = transport[key].decode; + } + return parse(data, revivers); +} +async function run_remote_function(event, state, allow_cookies, get_input, fn) { + const store = { + event: { + ...event, + setHeaders: () => { + throw new Error("setHeaders is not allowed in remote functions"); + }, + cookies: { + ...event.cookies, + set: (name, value, opts) => { + if (!allow_cookies) { + throw new Error("Cannot set cookies in `query` or `prerender` functions"); + } + if (opts.path && !opts.path.startsWith("/")) { + throw new Error("Cookies set in remote functions must have an absolute path"); + } + return event.cookies.set(name, value, opts); + }, + delete: (name, opts) => { + if (!allow_cookies) { + throw new Error("Cannot delete cookies in `query` or `prerender` functions"); + } + if (opts.path && !opts.path.startsWith("/")) { + throw new Error("Cookies deleted in remote functions must have an absolute path"); + } + return event.cookies.delete(name, opts); + } + } + }, + state: { + ...state, + is_in_remote_function: true + } + }; + const input = await with_request_store(store, get_input); + return with_request_store(store, () => fn(input)); +} +function get_cache(info, state = get_request_store().state) { + let cache = state.remote_data?.get(info); + if (cache === void 0) { + cache = {}; + (state.remote_data ??= /* @__PURE__ */ new Map()).set(info, cache); + } + return cache; +} +// @__NO_SIDE_EFFECTS__ +function query(validate_or_fn, maybe_fn) { + const fn = maybe_fn ?? validate_or_fn; + const validate = create_validator(validate_or_fn, maybe_fn); + const __ = { type: "query", id: "", name: "" }; + const wrapper = (arg) => { + if (prerendering) { + throw new Error( + `Cannot call query '${__.name}' while prerendering, as prerendered pages need static data. Use 'prerender' from $app/server instead` + ); + } + const { event, state } = get_request_store(); + const get_remote_function_result = () => run_remote_function(event, state, false, () => validate(arg), fn); + const promise = get_response(__, arg, state, get_remote_function_result); + promise.catch(() => { + }); + promise.set = (value) => update_refresh_value(get_refresh_context(__, "set", arg), value); + promise.refresh = () => { + const refresh_context = get_refresh_context(__, "refresh", arg); + const is_immediate_refresh = !refresh_context.cache[refresh_context.cache_key]; + const value = is_immediate_refresh ? promise : get_remote_function_result(); + return update_refresh_value(refresh_context, value, is_immediate_refresh); + }; + promise.withOverride = () => { + throw new Error(`Cannot call '${__.name}.withOverride()' on the server`); + }; + return ( + /** @type {RemoteQuery} */ + promise + ); + }; + Object.defineProperty(wrapper, "__", { value: __ }); + return wrapper; +} +// @__NO_SIDE_EFFECTS__ +function batch(validate_or_fn, maybe_fn) { + const fn = maybe_fn ?? validate_or_fn; + const validate = create_validator(validate_or_fn, maybe_fn); + const __ = { + type: "query_batch", + id: "", + name: "", + run: async (args, options) => { + const { event, state } = get_request_store(); + return run_remote_function( + event, + state, + false, + async () => Promise.all(args.map(validate)), + async (input) => { + const get_result = await fn(input); + return Promise.all( + input.map(async (arg, i) => { + try { + return { type: "result", data: get_result(arg, i) }; + } catch (error2) { + return { + type: "error", + error: await handle_error_and_jsonify(event, state, options, error2), + status: error2 instanceof HttpError || error2 instanceof SvelteKitError ? error2.status : 500 + }; + } + }) + ); + } + ); + } + }; + let batching = { args: [], resolvers: [] }; + const wrapper = (arg) => { + if (prerendering) { + throw new Error( + `Cannot call query.batch '${__.name}' while prerendering, as prerendered pages need static data. Use 'prerender' from $app/server instead` + ); + } + const { event, state } = get_request_store(); + const get_remote_function_result = () => { + return new Promise((resolve, reject) => { + batching.args.push(arg); + batching.resolvers.push({ resolve, reject }); + if (batching.args.length > 1) return; + setTimeout(async () => { + const batched = batching; + batching = { args: [], resolvers: [] }; + try { + return await run_remote_function( + event, + state, + false, + async () => Promise.all(batched.args.map(validate)), + async (input) => { + const get_result = await fn(input); + for (let i = 0; i < batched.resolvers.length; i++) { + try { + batched.resolvers[i].resolve(get_result(input[i], i)); + } catch (error2) { + batched.resolvers[i].reject(error2); + } + } + } + ); + } catch (error2) { + for (const resolver of batched.resolvers) { + resolver.reject(error2); + } + } + }, 0); + }); + }; + const promise = get_response(__, arg, state, get_remote_function_result); + promise.catch(() => { + }); + promise.set = (value) => update_refresh_value(get_refresh_context(__, "set", arg), value); + promise.refresh = () => { + const refresh_context = get_refresh_context(__, "refresh", arg); + const is_immediate_refresh = !refresh_context.cache[refresh_context.cache_key]; + const value = is_immediate_refresh ? promise : get_remote_function_result(); + return update_refresh_value(refresh_context, value, is_immediate_refresh); + }; + promise.withOverride = () => { + throw new Error(`Cannot call '${__.name}.withOverride()' on the server`); + }; + return ( + /** @type {RemoteQuery} */ + promise + ); + }; + Object.defineProperty(wrapper, "__", { value: __ }); + return wrapper; +} +Object.defineProperty(query, "batch", { value: batch, enumerable: true }); +function get_refresh_context(__, action, arg) { + const { state } = get_request_store(); + const { refreshes } = state; + if (!refreshes) { + const name = __.type === "query_batch" ? `query.batch '${__.name}'` : `query '${__.name}'`; + throw new Error( + `Cannot call ${action} on ${name} because it is not executed in the context of a command/form remote function` + ); + } + const cache = get_cache(__, state); + const cache_key = stringify_remote_arg(arg, state.transport); + const refreshes_key = create_remote_key(__.id, cache_key); + return { __, state, refreshes, refreshes_key, cache, cache_key }; +} +function update_refresh_value({ __, refreshes, refreshes_key, cache, cache_key }, value, is_immediate_refresh = false) { + const promise = Promise.resolve(value); + if (!is_immediate_refresh) { + cache[cache_key] = promise; + } + if (__.id) { + refreshes[refreshes_key] = promise; + } + return promise.then(() => { + }); +} +export { + get_response as a, + create_validator as c, + get_cache as g, + parse_remote_response as p, + query as q, + run_remote_function as r +}; diff --git a/.svelte-kit/output/server/chunks/root.js b/.svelte-kit/output/server/chunks/root.js new file mode 100644 index 0000000..c678a5c --- /dev/null +++ b/.svelte-kit/output/server/chunks/root.js @@ -0,0 +1,2713 @@ +import { H as HYDRATION_ERROR, C as COMMENT_NODE, f as HYDRATION_END, h as HYDRATION_START, i as HYDRATION_START_ELSE, r as run_all, B as BOUNDARY_EFFECT, R as REACTION_RAN, E as ERROR_VALUE, j as EFFECT, k as CONNECTED, l as CLEAN, M as MAYBE_DIRTY, D as DIRTY, m as DERIVED, W as WAS_MARKED, I as INERT, o as BLOCK_EFFECT, U as UNINITIALIZED, p as deferred, q as BRANCH_EFFECT, t as ROOT_EFFECT, u as RENDER_EFFECT, v as MANAGED_EFFECT, w as HEAD_EFFECT, x as DESTROYED, A as ASYNC, y as includes, z as HYDRATION_START_FAILED, F as EFFECT_TRANSPARENT, G as EFFECT_PRESERVED, S as STALE_REACTION, n as noop, J as EAGER_EFFECT, K as STATE_SYMBOL, L as object_prototype, N as array_prototype, O as get_descriptor, P as get_prototype_of, Q as is_array, T as is_extensible, V as USER_EFFECT, X as REACTION_IS_UPDATING, Y as index_of, Z as define_property, _ as array_from, $ as is_passive_event, a0 as LEGACY_PROPS, a1 as render, a2 as setContext, d as derived } from "./index3.js"; +import { D as DEV } from "./false.js"; +function equals(value) { + return value === this.v; +} +function safe_not_equal(a, b) { + return a != a ? b == b : a !== b || a !== null && typeof a === "object" || typeof a === "function"; +} +function safe_equals(value) { + return !safe_not_equal(value, this.v); +} +function effect_update_depth_exceeded() { + { + throw new Error(`https://svelte.dev/e/effect_update_depth_exceeded`); + } +} +function hydration_failed() { + { + throw new Error(`https://svelte.dev/e/hydration_failed`); + } +} +function state_descriptors_fixed() { + { + throw new Error(`https://svelte.dev/e/state_descriptors_fixed`); + } +} +function state_prototype_fixed() { + { + throw new Error(`https://svelte.dev/e/state_prototype_fixed`); + } +} +function state_unsafe_mutation() { + { + throw new Error(`https://svelte.dev/e/state_unsafe_mutation`); + } +} +function svelte_boundary_reset_onerror() { + { + throw new Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`); + } +} +function hydration_mismatch(location) { + { + console.warn(`https://svelte.dev/e/hydration_mismatch`); + } +} +function svelte_boundary_reset_noop() { + { + console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`); + } +} +let hydrating = false; +function set_hydrating(value) { + hydrating = value; +} +let hydrate_node; +function set_hydrate_node(node) { + if (node === null) { + hydration_mismatch(); + throw HYDRATION_ERROR; + } + return hydrate_node = node; +} +function hydrate_next() { + return set_hydrate_node(/* @__PURE__ */ get_next_sibling(hydrate_node)); +} +function next(count = 1) { + if (hydrating) { + var i = count; + var node = hydrate_node; + while (i--) { + node = /** @type {TemplateNode} */ + /* @__PURE__ */ get_next_sibling(node); + } + hydrate_node = node; + } +} +function skip_nodes(remove = true) { + var depth = 0; + var node = hydrate_node; + while (true) { + if (node.nodeType === COMMENT_NODE) { + var data = ( + /** @type {Comment} */ + node.data + ); + if (data === HYDRATION_END) { + if (depth === 0) return node; + depth -= 1; + } else if (data === HYDRATION_START || data === HYDRATION_START_ELSE || // "[1", "[2", etc. for if blocks + data[0] === "[" && !isNaN(Number(data.slice(1)))) { + depth += 1; + } + } + var next2 = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ get_next_sibling(node) + ); + if (remove) node.remove(); + node = next2; + } +} +let tracing_mode_flag = false; +let component_context = null; +function set_component_context(context) { + component_context = context; +} +function push(props, runes = false, fn) { + component_context = { + p: component_context, + i: false, + c: null, + e: null, + s: props, + x: null, + l: null + }; +} +function pop(component) { + var context = ( + /** @type {ComponentContext} */ + component_context + ); + var effects = context.e; + if (effects !== null) { + context.e = null; + for (var fn of effects) { + create_user_effect(fn); + } + } + context.i = true; + component_context = context.p; + return ( + /** @type {T} */ + {} + ); +} +function is_runes() { + return true; +} +let micro_tasks = []; +function run_micro_tasks() { + var tasks = micro_tasks; + micro_tasks = []; + run_all(tasks); +} +function queue_micro_task(fn) { + if (micro_tasks.length === 0 && !is_flushing_sync) { + var tasks = micro_tasks; + queueMicrotask(() => { + if (tasks === micro_tasks) run_micro_tasks(); + }); + } + micro_tasks.push(fn); +} +function flush_tasks() { + while (micro_tasks.length > 0) { + run_micro_tasks(); + } +} +function handle_error(error) { + var effect = active_effect; + if (effect === null) { + active_reaction.f |= ERROR_VALUE; + return error; + } + if ((effect.f & REACTION_RAN) === 0 && (effect.f & EFFECT) === 0) { + throw error; + } + invoke_error_boundary(error, effect); +} +function invoke_error_boundary(error, effect) { + while (effect !== null) { + if ((effect.f & BOUNDARY_EFFECT) !== 0) { + if ((effect.f & REACTION_RAN) === 0) { + throw error; + } + try { + effect.b.error(error); + return; + } catch (e) { + error = e; + } + } + effect = effect.parent; + } + throw error; +} +const STATUS_MASK = -7169; +function set_signal_status(signal, status) { + signal.f = signal.f & STATUS_MASK | status; +} +function update_derived_status(derived2) { + if ((derived2.f & CONNECTED) !== 0 || derived2.deps === null) { + set_signal_status(derived2, CLEAN); + } else { + set_signal_status(derived2, MAYBE_DIRTY); + } +} +function clear_marked(deps) { + if (deps === null) return; + for (const dep of deps) { + if ((dep.f & DERIVED) === 0 || (dep.f & WAS_MARKED) === 0) { + continue; + } + dep.f ^= WAS_MARKED; + clear_marked( + /** @type {Derived} */ + dep.deps + ); + } +} +function defer_effect(effect, dirty_effects, maybe_dirty_effects) { + if ((effect.f & DIRTY) !== 0) { + dirty_effects.add(effect); + } else if ((effect.f & MAYBE_DIRTY) !== 0) { + maybe_dirty_effects.add(effect); + } + clear_marked(effect.deps); + set_signal_status(effect, CLEAN); +} +const batches = /* @__PURE__ */ new Set(); +let current_batch = null; +let batch_values = null; +let queued_root_effects = []; +let last_scheduled_effect = null; +let is_flushing = false; +let is_flushing_sync = false; +class Batch { + /** + * The current values of any sources that are updated in this batch + * They keys of this map are identical to `this.#previous` + * @type {Map} + */ + current = /* @__PURE__ */ new Map(); + /** + * The values of any sources that are updated in this batch _before_ those updates took place. + * They keys of this map are identical to `this.#current` + * @type {Map} + */ + previous = /* @__PURE__ */ new Map(); + /** + * When the batch is committed (and the DOM is updated), we need to remove old branches + * and append new ones by calling the functions added inside (if/each/key/etc) blocks + * @type {Set<() => void>} + */ + #commit_callbacks = /* @__PURE__ */ new Set(); + /** + * If a fork is discarded, we need to destroy any effects that are no longer needed + * @type {Set<(batch: Batch) => void>} + */ + #discard_callbacks = /* @__PURE__ */ new Set(); + /** + * The number of async effects that are currently in flight + */ + #pending = 0; + /** + * The number of async effects that are currently in flight, _not_ inside a pending boundary + */ + #blocking_pending = 0; + /** + * A deferred that resolves when the batch is committed, used with `settled()` + * TODO replace with Promise.withResolvers once supported widely enough + * @type {{ promise: Promise, resolve: (value?: any) => void, reject: (reason: unknown) => void } | null} + */ + #deferred = null; + /** + * Deferred effects (which run after async work has completed) that are DIRTY + * @type {Set} + */ + #dirty_effects = /* @__PURE__ */ new Set(); + /** + * Deferred effects that are MAYBE_DIRTY + * @type {Set} + */ + #maybe_dirty_effects = /* @__PURE__ */ new Set(); + /** + * A map of branches that still exist, but will be destroyed when this batch + * is committed — we skip over these during `process`. + * The value contains child effects that were dirty/maybe_dirty before being reset, + * so they can be rescheduled if the branch survives. + * @type {Map} + */ + #skipped_branches = /* @__PURE__ */ new Map(); + is_fork = false; + #decrement_queued = false; + #is_deferred() { + return this.is_fork || this.#blocking_pending > 0; + } + /** + * Add an effect to the #skipped_branches map and reset its children + * @param {Effect} effect + */ + skip_effect(effect) { + if (!this.#skipped_branches.has(effect)) { + this.#skipped_branches.set(effect, { d: [], m: [] }); + } + } + /** + * Remove an effect from the #skipped_branches map and reschedule + * any tracked dirty/maybe_dirty child effects + * @param {Effect} effect + */ + unskip_effect(effect) { + var tracked = this.#skipped_branches.get(effect); + if (tracked) { + this.#skipped_branches.delete(effect); + for (var e of tracked.d) { + set_signal_status(e, DIRTY); + schedule_effect(e); + } + for (e of tracked.m) { + set_signal_status(e, MAYBE_DIRTY); + schedule_effect(e); + } + } + } + /** + * + * @param {Effect[]} root_effects + */ + process(root_effects) { + queued_root_effects = []; + this.apply(); + var effects = []; + var render_effects = []; + for (const root2 of root_effects) { + this.#traverse_effect_tree(root2, effects, render_effects); + } + if (this.#is_deferred()) { + this.#defer_effects(render_effects); + this.#defer_effects(effects); + for (const [e, t] of this.#skipped_branches) { + reset_branch(e, t); + } + } else { + for (const fn of this.#commit_callbacks) fn(); + this.#commit_callbacks.clear(); + if (this.#pending === 0) { + this.#commit(); + } + current_batch = null; + flush_queued_effects(render_effects); + flush_queued_effects(effects); + this.#dirty_effects.clear(); + this.#maybe_dirty_effects.clear(); + this.#deferred?.resolve(); + } + batch_values = null; + } + /** + * Traverse the effect tree, executing effects or stashing + * them for later execution as appropriate + * @param {Effect} root + * @param {Effect[]} effects + * @param {Effect[]} render_effects + */ + #traverse_effect_tree(root2, effects, render_effects) { + root2.f ^= CLEAN; + var effect = root2.first; + while (effect !== null) { + var flags2 = effect.f; + var is_branch = (flags2 & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0; + var is_skippable_branch = is_branch && (flags2 & CLEAN) !== 0; + var skip = is_skippable_branch || (flags2 & INERT) !== 0 || this.#skipped_branches.has(effect); + if (!skip && effect.fn !== null) { + if (is_branch) { + effect.f ^= CLEAN; + } else if ((flags2 & EFFECT) !== 0) { + effects.push(effect); + } else if (is_dirty(effect)) { + if ((flags2 & BLOCK_EFFECT) !== 0) this.#maybe_dirty_effects.add(effect); + update_effect(effect); + } + var child = effect.first; + if (child !== null) { + effect = child; + continue; + } + } + while (effect !== null) { + var next2 = effect.next; + if (next2 !== null) { + effect = next2; + break; + } + effect = effect.parent; + } + } + } + /** + * @param {Effect[]} effects + */ + #defer_effects(effects) { + for (var i = 0; i < effects.length; i += 1) { + defer_effect(effects[i], this.#dirty_effects, this.#maybe_dirty_effects); + } + } + /** + * Associate a change to a given source with the current + * batch, noting its previous and current values + * @param {Source} source + * @param {any} value + */ + capture(source2, value) { + if (value !== UNINITIALIZED && !this.previous.has(source2)) { + this.previous.set(source2, value); + } + if ((source2.f & ERROR_VALUE) === 0) { + this.current.set(source2, source2.v); + batch_values?.set(source2, source2.v); + } + } + activate() { + current_batch = this; + this.apply(); + } + deactivate() { + if (current_batch !== this) return; + current_batch = null; + batch_values = null; + } + flush() { + this.activate(); + if (queued_root_effects.length > 0) { + flush_effects(); + if (current_batch !== null && current_batch !== this) { + return; + } + } else if (this.#pending === 0) { + this.process([]); + } + this.deactivate(); + } + discard() { + for (const fn of this.#discard_callbacks) fn(this); + this.#discard_callbacks.clear(); + } + #commit() { + if (batches.size > 1) { + this.previous.clear(); + var previous_batch_values = batch_values; + var is_earlier = true; + for (const batch of batches) { + if (batch === this) { + is_earlier = false; + continue; + } + const sources = []; + for (const [source2, value] of this.current) { + if (batch.current.has(source2)) { + if (is_earlier && value !== batch.current.get(source2)) { + batch.current.set(source2, value); + } else { + continue; + } + } + sources.push(source2); + } + if (sources.length === 0) { + continue; + } + const others = [...batch.current.keys()].filter((s) => !this.current.has(s)); + if (others.length > 0) { + var prev_queued_root_effects = queued_root_effects; + queued_root_effects = []; + const marked = /* @__PURE__ */ new Set(); + const checked = /* @__PURE__ */ new Map(); + for (const source2 of sources) { + mark_effects(source2, others, marked, checked); + } + if (queued_root_effects.length > 0) { + current_batch = batch; + batch.apply(); + for (const root2 of queued_root_effects) { + batch.#traverse_effect_tree(root2, [], []); + } + batch.deactivate(); + } + queued_root_effects = prev_queued_root_effects; + } + } + current_batch = null; + batch_values = previous_batch_values; + } + this.#skipped_branches.clear(); + batches.delete(this); + } + /** + * + * @param {boolean} blocking + */ + increment(blocking) { + this.#pending += 1; + if (blocking) this.#blocking_pending += 1; + } + /** + * + * @param {boolean} blocking + */ + decrement(blocking) { + this.#pending -= 1; + if (blocking) this.#blocking_pending -= 1; + if (this.#decrement_queued) return; + this.#decrement_queued = true; + queue_micro_task(() => { + this.#decrement_queued = false; + if (!this.#is_deferred()) { + this.revive(); + } else if (queued_root_effects.length > 0) { + this.flush(); + } + }); + } + revive() { + for (const e of this.#dirty_effects) { + this.#maybe_dirty_effects.delete(e); + set_signal_status(e, DIRTY); + schedule_effect(e); + } + for (const e of this.#maybe_dirty_effects) { + set_signal_status(e, MAYBE_DIRTY); + schedule_effect(e); + } + this.flush(); + } + /** @param {() => void} fn */ + oncommit(fn) { + this.#commit_callbacks.add(fn); + } + /** @param {(batch: Batch) => void} fn */ + ondiscard(fn) { + this.#discard_callbacks.add(fn); + } + settled() { + return (this.#deferred ??= deferred()).promise; + } + static ensure() { + if (current_batch === null) { + const batch = current_batch = new Batch(); + batches.add(current_batch); + if (!is_flushing_sync) { + queue_micro_task(() => { + if (current_batch !== batch) { + return; + } + batch.flush(); + }); + } + } + return current_batch; + } + apply() { + return; + } +} +function flushSync(fn) { + var was_flushing_sync = is_flushing_sync; + is_flushing_sync = true; + try { + var result; + if (fn) ; + while (true) { + flush_tasks(); + if (queued_root_effects.length === 0) { + current_batch?.flush(); + if (queued_root_effects.length === 0) { + last_scheduled_effect = null; + return ( + /** @type {T} */ + result + ); + } + } + flush_effects(); + } + } finally { + is_flushing_sync = was_flushing_sync; + } +} +function flush_effects() { + is_flushing = true; + var source_stacks = null; + try { + var flush_count = 0; + while (queued_root_effects.length > 0) { + var batch = Batch.ensure(); + if (flush_count++ > 1e3) { + var updates, entry; + if (DEV) ; + infinite_loop_guard(); + } + batch.process(queued_root_effects); + old_values.clear(); + if (DEV) ; + } + } finally { + queued_root_effects = []; + is_flushing = false; + last_scheduled_effect = null; + } +} +function infinite_loop_guard() { + try { + effect_update_depth_exceeded(); + } catch (error) { + invoke_error_boundary(error, last_scheduled_effect); + } +} +let eager_block_effects = null; +function flush_queued_effects(effects) { + var length = effects.length; + if (length === 0) return; + var i = 0; + while (i < length) { + var effect = effects[i++]; + if ((effect.f & (DESTROYED | INERT)) === 0 && is_dirty(effect)) { + eager_block_effects = /* @__PURE__ */ new Set(); + update_effect(effect); + if (effect.deps === null && effect.first === null && effect.nodes === null && effect.teardown === null && effect.ac === null) { + unlink_effect(effect); + } + if (eager_block_effects?.size > 0) { + old_values.clear(); + for (const e of eager_block_effects) { + if ((e.f & (DESTROYED | INERT)) !== 0) continue; + const ordered_effects = [e]; + let ancestor = e.parent; + while (ancestor !== null) { + if (eager_block_effects.has(ancestor)) { + eager_block_effects.delete(ancestor); + ordered_effects.push(ancestor); + } + ancestor = ancestor.parent; + } + for (let j = ordered_effects.length - 1; j >= 0; j--) { + const e2 = ordered_effects[j]; + if ((e2.f & (DESTROYED | INERT)) !== 0) continue; + update_effect(e2); + } + } + eager_block_effects.clear(); + } + } + } + eager_block_effects = null; +} +function mark_effects(value, sources, marked, checked) { + if (marked.has(value)) return; + marked.add(value); + if (value.reactions !== null) { + for (const reaction of value.reactions) { + const flags2 = reaction.f; + if ((flags2 & DERIVED) !== 0) { + mark_effects( + /** @type {Derived} */ + reaction, + sources, + marked, + checked + ); + } else if ((flags2 & (ASYNC | BLOCK_EFFECT)) !== 0 && (flags2 & DIRTY) === 0 && depends_on(reaction, sources, checked)) { + set_signal_status(reaction, DIRTY); + schedule_effect( + /** @type {Effect} */ + reaction + ); + } + } + } +} +function depends_on(reaction, sources, checked) { + const depends = checked.get(reaction); + if (depends !== void 0) return depends; + if (reaction.deps !== null) { + for (const dep of reaction.deps) { + if (includes.call(sources, dep)) { + return true; + } + if ((dep.f & DERIVED) !== 0 && depends_on( + /** @type {Derived} */ + dep, + sources, + checked + )) { + checked.set( + /** @type {Derived} */ + dep, + true + ); + return true; + } + } + } + checked.set(reaction, false); + return false; +} +function schedule_effect(signal) { + var effect = last_scheduled_effect = signal; + var boundary2 = effect.b; + if (boundary2?.is_pending && (signal.f & (EFFECT | RENDER_EFFECT | MANAGED_EFFECT)) !== 0 && (signal.f & REACTION_RAN) === 0) { + boundary2.defer_effect(signal); + return; + } + while (effect.parent !== null) { + effect = effect.parent; + var flags2 = effect.f; + if (is_flushing && effect === active_effect && (flags2 & BLOCK_EFFECT) !== 0 && (flags2 & HEAD_EFFECT) === 0 && (flags2 & REACTION_RAN) !== 0) { + return; + } + if ((flags2 & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) { + if ((flags2 & CLEAN) === 0) { + return; + } + effect.f ^= CLEAN; + } + } + queued_root_effects.push(effect); +} +function reset_branch(effect, tracked) { + if ((effect.f & BRANCH_EFFECT) !== 0 && (effect.f & CLEAN) !== 0) { + return; + } + if ((effect.f & DIRTY) !== 0) { + tracked.d.push(effect); + } else if ((effect.f & MAYBE_DIRTY) !== 0) { + tracked.m.push(effect); + } + set_signal_status(effect, CLEAN); + var e = effect.first; + while (e !== null) { + reset_branch(e, tracked); + e = e.next; + } +} +function createSubscriber(start) { + let subscribers = 0; + let version = source(0); + let stop; + return () => { + if (effect_tracking()) { + get(version); + render_effect(() => { + if (subscribers === 0) { + stop = untrack(() => start(() => increment(version))); + } + subscribers += 1; + return () => { + queue_micro_task(() => { + subscribers -= 1; + if (subscribers === 0) { + stop?.(); + stop = void 0; + increment(version); + } + }); + }; + }); + } + }; +} +var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED; +function boundary(node, props, children, transform_error) { + new Boundary(node, props, children, transform_error); +} +class Boundary { + /** @type {Boundary | null} */ + parent; + is_pending = false; + /** + * API-level transformError transform function. Transforms errors before they reach the `failed` snippet. + * Inherited from parent boundary, or defaults to identity. + * @type {(error: unknown) => unknown} + */ + transform_error; + /** @type {TemplateNode} */ + #anchor; + /** @type {TemplateNode | null} */ + #hydrate_open = hydrating ? hydrate_node : null; + /** @type {BoundaryProps} */ + #props; + /** @type {((anchor: Node) => void)} */ + #children; + /** @type {Effect} */ + #effect; + /** @type {Effect | null} */ + #main_effect = null; + /** @type {Effect | null} */ + #pending_effect = null; + /** @type {Effect | null} */ + #failed_effect = null; + /** @type {DocumentFragment | null} */ + #offscreen_fragment = null; + #local_pending_count = 0; + #pending_count = 0; + #pending_count_update_queued = false; + /** @type {Set} */ + #dirty_effects = /* @__PURE__ */ new Set(); + /** @type {Set} */ + #maybe_dirty_effects = /* @__PURE__ */ new Set(); + /** + * A source containing the number of pending async deriveds/expressions. + * Only created if `$effect.pending()` is used inside the boundary, + * otherwise updating the source results in needless `Batch.ensure()` + * calls followed by no-op flushes + * @type {Source | null} + */ + #effect_pending = null; + #effect_pending_subscriber = createSubscriber(() => { + this.#effect_pending = source(this.#local_pending_count); + return () => { + this.#effect_pending = null; + }; + }); + /** + * @param {TemplateNode} node + * @param {BoundaryProps} props + * @param {((anchor: Node) => void)} children + * @param {((error: unknown) => unknown) | undefined} [transform_error] + */ + constructor(node, props, children, transform_error) { + this.#anchor = node; + this.#props = props; + this.#children = (anchor) => { + var effect = ( + /** @type {Effect} */ + active_effect + ); + effect.b = this; + effect.f |= BOUNDARY_EFFECT; + children(anchor); + }; + this.parent = /** @type {Effect} */ + active_effect.b; + this.transform_error = transform_error ?? this.parent?.transform_error ?? ((e) => e); + this.#effect = block(() => { + if (hydrating) { + const comment = ( + /** @type {Comment} */ + this.#hydrate_open + ); + hydrate_next(); + const server_rendered_pending = comment.data === HYDRATION_START_ELSE; + const server_rendered_failed = comment.data.startsWith(HYDRATION_START_FAILED); + if (server_rendered_failed) { + const serialized_error = JSON.parse(comment.data.slice(HYDRATION_START_FAILED.length)); + this.#hydrate_failed_content(serialized_error); + } else if (server_rendered_pending) { + this.#hydrate_pending_content(); + } else { + this.#hydrate_resolved_content(); + } + } else { + this.#render(); + } + }, flags); + if (hydrating) { + this.#anchor = hydrate_node; + } + } + #hydrate_resolved_content() { + try { + this.#main_effect = branch(() => this.#children(this.#anchor)); + } catch (error) { + this.error(error); + } + } + /** + * @param {unknown} error The deserialized error from the server's hydration comment + */ + #hydrate_failed_content(error) { + const failed = this.#props.failed; + if (!failed) return; + this.#failed_effect = branch(() => { + failed( + this.#anchor, + () => error, + () => () => { + } + ); + }); + } + #hydrate_pending_content() { + const pending = this.#props.pending; + if (!pending) return; + this.is_pending = true; + this.#pending_effect = branch(() => pending(this.#anchor)); + queue_micro_task(() => { + var fragment = this.#offscreen_fragment = document.createDocumentFragment(); + var anchor = create_text(); + fragment.append(anchor); + this.#main_effect = this.#run(() => { + Batch.ensure(); + return branch(() => this.#children(anchor)); + }); + if (this.#pending_count === 0) { + this.#anchor.before(fragment); + this.#offscreen_fragment = null; + pause_effect( + /** @type {Effect} */ + this.#pending_effect, + () => { + this.#pending_effect = null; + } + ); + this.#resolve(); + } + }); + } + #render() { + try { + this.is_pending = this.has_pending_snippet(); + this.#pending_count = 0; + this.#local_pending_count = 0; + this.#main_effect = branch(() => { + this.#children(this.#anchor); + }); + if (this.#pending_count > 0) { + var fragment = this.#offscreen_fragment = document.createDocumentFragment(); + move_effect(this.#main_effect, fragment); + const pending = ( + /** @type {(anchor: Node) => void} */ + this.#props.pending + ); + this.#pending_effect = branch(() => pending(this.#anchor)); + } else { + this.#resolve(); + } + } catch (error) { + this.error(error); + } + } + #resolve() { + this.is_pending = false; + for (const e of this.#dirty_effects) { + set_signal_status(e, DIRTY); + schedule_effect(e); + } + for (const e of this.#maybe_dirty_effects) { + set_signal_status(e, MAYBE_DIRTY); + schedule_effect(e); + } + this.#dirty_effects.clear(); + this.#maybe_dirty_effects.clear(); + } + /** + * Defer an effect inside a pending boundary until the boundary resolves + * @param {Effect} effect + */ + defer_effect(effect) { + defer_effect(effect, this.#dirty_effects, this.#maybe_dirty_effects); + } + /** + * Returns `false` if the effect exists inside a boundary whose pending snippet is shown + * @returns {boolean} + */ + is_rendered() { + return !this.is_pending && (!this.parent || this.parent.is_rendered()); + } + has_pending_snippet() { + return !!this.#props.pending; + } + /** + * @template T + * @param {() => T} fn + */ + #run(fn) { + var previous_effect = active_effect; + var previous_reaction = active_reaction; + var previous_ctx = component_context; + set_active_effect(this.#effect); + set_active_reaction(this.#effect); + set_component_context(this.#effect.ctx); + try { + return fn(); + } catch (e) { + handle_error(e); + return null; + } finally { + set_active_effect(previous_effect); + set_active_reaction(previous_reaction); + set_component_context(previous_ctx); + } + } + /** + * Updates the pending count associated with the currently visible pending snippet, + * if any, such that we can replace the snippet with content once work is done + * @param {1 | -1} d + */ + #update_pending_count(d) { + if (!this.has_pending_snippet()) { + if (this.parent) { + this.parent.#update_pending_count(d); + } + return; + } + this.#pending_count += d; + if (this.#pending_count === 0) { + this.#resolve(); + if (this.#pending_effect) { + pause_effect(this.#pending_effect, () => { + this.#pending_effect = null; + }); + } + if (this.#offscreen_fragment) { + this.#anchor.before(this.#offscreen_fragment); + this.#offscreen_fragment = null; + } + } + } + /** + * Update the source that powers `$effect.pending()` inside this boundary, + * and controls when the current `pending` snippet (if any) is removed. + * Do not call from inside the class + * @param {1 | -1} d + */ + update_pending_count(d) { + this.#update_pending_count(d); + this.#local_pending_count += d; + if (!this.#effect_pending || this.#pending_count_update_queued) return; + this.#pending_count_update_queued = true; + queue_micro_task(() => { + this.#pending_count_update_queued = false; + if (this.#effect_pending) { + internal_set(this.#effect_pending, this.#local_pending_count); + } + }); + } + get_effect_pending() { + this.#effect_pending_subscriber(); + return get( + /** @type {Source} */ + this.#effect_pending + ); + } + /** @param {unknown} error */ + error(error) { + var onerror = this.#props.onerror; + let failed = this.#props.failed; + if (!onerror && !failed) { + throw error; + } + if (this.#main_effect) { + destroy_effect(this.#main_effect); + this.#main_effect = null; + } + if (this.#pending_effect) { + destroy_effect(this.#pending_effect); + this.#pending_effect = null; + } + if (this.#failed_effect) { + destroy_effect(this.#failed_effect); + this.#failed_effect = null; + } + if (hydrating) { + set_hydrate_node( + /** @type {TemplateNode} */ + this.#hydrate_open + ); + next(); + set_hydrate_node(skip_nodes()); + } + var did_reset = false; + var calling_on_error = false; + const reset = () => { + if (did_reset) { + svelte_boundary_reset_noop(); + return; + } + did_reset = true; + if (calling_on_error) { + svelte_boundary_reset_onerror(); + } + if (this.#failed_effect !== null) { + pause_effect(this.#failed_effect, () => { + this.#failed_effect = null; + }); + } + this.#run(() => { + Batch.ensure(); + this.#render(); + }); + }; + const handle_error_result = (transformed_error) => { + try { + calling_on_error = true; + onerror?.(transformed_error, reset); + calling_on_error = false; + } catch (error2) { + invoke_error_boundary(error2, this.#effect && this.#effect.parent); + } + if (failed) { + this.#failed_effect = this.#run(() => { + Batch.ensure(); + try { + return branch(() => { + var effect = ( + /** @type {Effect} */ + active_effect + ); + effect.b = this; + effect.f |= BOUNDARY_EFFECT; + failed( + this.#anchor, + () => transformed_error, + () => reset + ); + }); + } catch (error2) { + invoke_error_boundary( + error2, + /** @type {Effect} */ + this.#effect.parent + ); + return null; + } + }); + } + }; + queue_micro_task(() => { + var result; + try { + result = this.transform_error(error); + } catch (e) { + invoke_error_boundary(e, this.#effect && this.#effect.parent); + return; + } + if (result !== null && typeof result === "object" && typeof /** @type {any} */ + result.then === "function") { + result.then( + handle_error_result, + /** @param {unknown} e */ + (e) => invoke_error_boundary(e, this.#effect && this.#effect.parent) + ); + } else { + handle_error_result(result); + } + }); + } +} +function destroy_derived_effects(derived2) { + var effects = derived2.effects; + if (effects !== null) { + derived2.effects = null; + for (var i = 0; i < effects.length; i += 1) { + destroy_effect( + /** @type {Effect} */ + effects[i] + ); + } + } +} +function get_derived_parent_effect(derived2) { + var parent = derived2.parent; + while (parent !== null) { + if ((parent.f & DERIVED) === 0) { + return (parent.f & DESTROYED) === 0 ? ( + /** @type {Effect} */ + parent + ) : null; + } + parent = parent.parent; + } + return null; +} +function execute_derived(derived2) { + var value; + var prev_active_effect = active_effect; + set_active_effect(get_derived_parent_effect(derived2)); + { + try { + derived2.f &= ~WAS_MARKED; + destroy_derived_effects(derived2); + value = update_reaction(derived2); + } finally { + set_active_effect(prev_active_effect); + } + } + return value; +} +function update_derived(derived2) { + var value = execute_derived(derived2); + if (!derived2.equals(value)) { + derived2.wv = increment_write_version(); + if (!current_batch?.is_fork || derived2.deps === null) { + derived2.v = value; + if (derived2.deps === null) { + set_signal_status(derived2, CLEAN); + return; + } + } + } + if (is_destroying_effect) { + return; + } + if (batch_values !== null) { + if (effect_tracking() || current_batch?.is_fork) { + batch_values.set(derived2, value); + } + } else { + update_derived_status(derived2); + } +} +function freeze_derived_effects(derived2) { + if (derived2.effects === null) return; + for (const e of derived2.effects) { + if (e.teardown || e.ac) { + e.teardown?.(); + e.ac?.abort(STALE_REACTION); + e.teardown = noop; + e.ac = null; + remove_reactions(e, 0); + destroy_effect_children(e); + } + } +} +function unfreeze_derived_effects(derived2) { + if (derived2.effects === null) return; + for (const e of derived2.effects) { + if (e.teardown) { + update_effect(e); + } + } +} +let eager_effects = /* @__PURE__ */ new Set(); +const old_values = /* @__PURE__ */ new Map(); +let eager_effects_deferred = false; +function source(v, stack) { + var signal = { + f: 0, + // TODO ideally we could skip this altogether, but it causes type errors + v, + reactions: null, + equals, + rv: 0, + wv: 0 + }; + return signal; +} +// @__NO_SIDE_EFFECTS__ +function state(v, stack) { + const s = source(v); + push_reaction_value(s); + return s; +} +// @__NO_SIDE_EFFECTS__ +function mutable_source(initial_value, immutable = false, trackable = true) { + const s = source(initial_value); + if (!immutable) { + s.equals = safe_equals; + } + return s; +} +function set(source2, value, should_proxy = false) { + if (active_reaction !== null && // since we are untracking the function inside `$inspect.with` we need to add this check + // to ensure we error if state is set inside an inspect effect + (!untracking || (active_reaction.f & EAGER_EFFECT) !== 0) && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT | ASYNC | EAGER_EFFECT)) !== 0 && (current_sources === null || !includes.call(current_sources, source2))) { + state_unsafe_mutation(); + } + let new_value = should_proxy ? proxy(value) : value; + return internal_set(source2, new_value); +} +function internal_set(source2, value) { + if (!source2.equals(value)) { + var old_value = source2.v; + if (is_destroying_effect) { + old_values.set(source2, value); + } else { + old_values.set(source2, old_value); + } + source2.v = value; + var batch = Batch.ensure(); + batch.capture(source2, old_value); + if ((source2.f & DERIVED) !== 0) { + const derived2 = ( + /** @type {Derived} */ + source2 + ); + if ((source2.f & DIRTY) !== 0) { + execute_derived(derived2); + } + update_derived_status(derived2); + } + source2.wv = increment_write_version(); + mark_reactions(source2, DIRTY); + if (active_effect !== null && (active_effect.f & CLEAN) !== 0 && (active_effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0) { + if (untracked_writes === null) { + set_untracked_writes([source2]); + } else { + untracked_writes.push(source2); + } + } + if (!batch.is_fork && eager_effects.size > 0 && !eager_effects_deferred) { + flush_eager_effects(); + } + } + return value; +} +function flush_eager_effects() { + eager_effects_deferred = false; + for (const effect of eager_effects) { + if ((effect.f & CLEAN) !== 0) { + set_signal_status(effect, MAYBE_DIRTY); + } + if (is_dirty(effect)) { + update_effect(effect); + } + } + eager_effects.clear(); +} +function increment(source2) { + set(source2, source2.v + 1); +} +function mark_reactions(signal, status) { + var reactions = signal.reactions; + if (reactions === null) return; + var length = reactions.length; + for (var i = 0; i < length; i++) { + var reaction = reactions[i]; + var flags2 = reaction.f; + var not_dirty = (flags2 & DIRTY) === 0; + if (not_dirty) { + set_signal_status(reaction, status); + } + if ((flags2 & DERIVED) !== 0) { + var derived2 = ( + /** @type {Derived} */ + reaction + ); + batch_values?.delete(derived2); + if ((flags2 & WAS_MARKED) === 0) { + if (flags2 & CONNECTED) { + reaction.f |= WAS_MARKED; + } + mark_reactions(derived2, MAYBE_DIRTY); + } + } else if (not_dirty) { + if ((flags2 & BLOCK_EFFECT) !== 0 && eager_block_effects !== null) { + eager_block_effects.add( + /** @type {Effect} */ + reaction + ); + } + schedule_effect( + /** @type {Effect} */ + reaction + ); + } + } +} +function proxy(value) { + if (typeof value !== "object" || value === null || STATE_SYMBOL in value) { + return value; + } + const prototype = get_prototype_of(value); + if (prototype !== object_prototype && prototype !== array_prototype) { + return value; + } + var sources = /* @__PURE__ */ new Map(); + var is_proxied_array = is_array(value); + var version = /* @__PURE__ */ state(0); + var parent_version = update_version; + var with_parent = (fn) => { + if (update_version === parent_version) { + return fn(); + } + var reaction = active_reaction; + var version2 = update_version; + set_active_reaction(null); + set_update_version(parent_version); + var result = fn(); + set_active_reaction(reaction); + set_update_version(version2); + return result; + }; + if (is_proxied_array) { + sources.set("length", /* @__PURE__ */ state( + /** @type {any[]} */ + value.length + )); + } + return new Proxy( + /** @type {any} */ + value, + { + defineProperty(_, prop, descriptor) { + if (!("value" in descriptor) || descriptor.configurable === false || descriptor.enumerable === false || descriptor.writable === false) { + state_descriptors_fixed(); + } + var s = sources.get(prop); + if (s === void 0) { + with_parent(() => { + var s2 = /* @__PURE__ */ state(descriptor.value); + sources.set(prop, s2); + return s2; + }); + } else { + set(s, descriptor.value, true); + } + return true; + }, + deleteProperty(target, prop) { + var s = sources.get(prop); + if (s === void 0) { + if (prop in target) { + const s2 = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED)); + sources.set(prop, s2); + increment(version); + } + } else { + set(s, UNINITIALIZED); + increment(version); + } + return true; + }, + get(target, prop, receiver) { + if (prop === STATE_SYMBOL) { + return value; + } + var s = sources.get(prop); + var exists = prop in target; + if (s === void 0 && (!exists || get_descriptor(target, prop)?.writable)) { + s = with_parent(() => { + var p = proxy(exists ? target[prop] : UNINITIALIZED); + var s2 = /* @__PURE__ */ state(p); + return s2; + }); + sources.set(prop, s); + } + if (s !== void 0) { + var v = get(s); + return v === UNINITIALIZED ? void 0 : v; + } + return Reflect.get(target, prop, receiver); + }, + getOwnPropertyDescriptor(target, prop) { + var descriptor = Reflect.getOwnPropertyDescriptor(target, prop); + if (descriptor && "value" in descriptor) { + var s = sources.get(prop); + if (s) descriptor.value = get(s); + } else if (descriptor === void 0) { + var source2 = sources.get(prop); + var value2 = source2?.v; + if (source2 !== void 0 && value2 !== UNINITIALIZED) { + return { + enumerable: true, + configurable: true, + value: value2, + writable: true + }; + } + } + return descriptor; + }, + has(target, prop) { + if (prop === STATE_SYMBOL) { + return true; + } + var s = sources.get(prop); + var has = s !== void 0 && s.v !== UNINITIALIZED || Reflect.has(target, prop); + if (s !== void 0 || active_effect !== null && (!has || get_descriptor(target, prop)?.writable)) { + if (s === void 0) { + s = with_parent(() => { + var p = has ? proxy(target[prop]) : UNINITIALIZED; + var s2 = /* @__PURE__ */ state(p); + return s2; + }); + sources.set(prop, s); + } + var value2 = get(s); + if (value2 === UNINITIALIZED) { + return false; + } + } + return has; + }, + set(target, prop, value2, receiver) { + var s = sources.get(prop); + var has = prop in target; + if (is_proxied_array && prop === "length") { + for (var i = value2; i < /** @type {Source} */ + s.v; i += 1) { + var other_s = sources.get(i + ""); + if (other_s !== void 0) { + set(other_s, UNINITIALIZED); + } else if (i in target) { + other_s = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED)); + sources.set(i + "", other_s); + } + } + } + if (s === void 0) { + if (!has || get_descriptor(target, prop)?.writable) { + s = with_parent(() => /* @__PURE__ */ state(void 0)); + set(s, proxy(value2)); + sources.set(prop, s); + } + } else { + has = s.v !== UNINITIALIZED; + var p = with_parent(() => proxy(value2)); + set(s, p); + } + var descriptor = Reflect.getOwnPropertyDescriptor(target, prop); + if (descriptor?.set) { + descriptor.set.call(receiver, value2); + } + if (!has) { + if (is_proxied_array && typeof prop === "string") { + var ls = ( + /** @type {Source} */ + sources.get("length") + ); + var n = Number(prop); + if (Number.isInteger(n) && n >= ls.v) { + set(ls, n + 1); + } + } + increment(version); + } + return true; + }, + ownKeys(target) { + get(version); + var own_keys = Reflect.ownKeys(target).filter((key2) => { + var source3 = sources.get(key2); + return source3 === void 0 || source3.v !== UNINITIALIZED; + }); + for (var [key, source2] of sources) { + if (source2.v !== UNINITIALIZED && !(key in target)) { + own_keys.push(key); + } + } + return own_keys; + }, + setPrototypeOf() { + state_prototype_fixed(); + } + } + ); +} +var $window; +var first_child_getter; +var next_sibling_getter; +function init_operations() { + if ($window !== void 0) { + return; + } + $window = window; + var element_prototype = Element.prototype; + var node_prototype = Node.prototype; + var text_prototype = Text.prototype; + first_child_getter = get_descriptor(node_prototype, "firstChild").get; + next_sibling_getter = get_descriptor(node_prototype, "nextSibling").get; + if (is_extensible(element_prototype)) { + element_prototype.__click = void 0; + element_prototype.__className = void 0; + element_prototype.__attributes = null; + element_prototype.__style = void 0; + element_prototype.__e = void 0; + } + if (is_extensible(text_prototype)) { + text_prototype.__t = void 0; + } +} +function create_text(value = "") { + return document.createTextNode(value); +} +// @__NO_SIDE_EFFECTS__ +function get_first_child(node) { + return ( + /** @type {TemplateNode | null} */ + first_child_getter.call(node) + ); +} +// @__NO_SIDE_EFFECTS__ +function get_next_sibling(node) { + return ( + /** @type {TemplateNode | null} */ + next_sibling_getter.call(node) + ); +} +function clear_text_content(node) { + node.textContent = ""; +} +function without_reactive_context(fn) { + var previous_reaction = active_reaction; + var previous_effect = active_effect; + set_active_reaction(null); + set_active_effect(null); + try { + return fn(); + } finally { + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); + } +} +function push_effect(effect, parent_effect) { + var parent_last = parent_effect.last; + if (parent_last === null) { + parent_effect.last = parent_effect.first = effect; + } else { + parent_last.next = effect; + effect.prev = parent_last; + parent_effect.last = effect; + } +} +function create_effect(type, fn, sync) { + var parent = active_effect; + if (parent !== null && (parent.f & INERT) !== 0) { + type |= INERT; + } + var effect = { + ctx: component_context, + deps: null, + nodes: null, + f: type | DIRTY | CONNECTED, + first: null, + fn, + last: null, + next: null, + parent, + b: parent && parent.b, + prev: null, + teardown: null, + wv: 0, + ac: null + }; + if (sync) { + try { + update_effect(effect); + } catch (e2) { + destroy_effect(effect); + throw e2; + } + } else if (fn !== null) { + schedule_effect(effect); + } + var e = effect; + if (sync && e.deps === null && e.teardown === null && e.nodes === null && e.first === e.last && // either `null`, or a singular child + (e.f & EFFECT_PRESERVED) === 0) { + e = e.first; + if ((type & BLOCK_EFFECT) !== 0 && (type & EFFECT_TRANSPARENT) !== 0 && e !== null) { + e.f |= EFFECT_TRANSPARENT; + } + } + if (e !== null) { + e.parent = parent; + if (parent !== null) { + push_effect(e, parent); + } + if (active_reaction !== null && (active_reaction.f & DERIVED) !== 0 && (type & ROOT_EFFECT) === 0) { + var derived2 = ( + /** @type {Derived} */ + active_reaction + ); + (derived2.effects ??= []).push(e); + } + } + return effect; +} +function effect_tracking() { + return active_reaction !== null && !untracking; +} +function create_user_effect(fn) { + return create_effect(EFFECT | USER_EFFECT, fn, false); +} +function component_root(fn) { + Batch.ensure(); + const effect = create_effect(ROOT_EFFECT | EFFECT_PRESERVED, fn, true); + return (options = {}) => { + return new Promise((fulfil) => { + if (options.outro) { + pause_effect(effect, () => { + destroy_effect(effect); + fulfil(void 0); + }); + } else { + destroy_effect(effect); + fulfil(void 0); + } + }); + }; +} +function render_effect(fn, flags2 = 0) { + return create_effect(RENDER_EFFECT | flags2, fn, true); +} +function block(fn, flags2 = 0) { + var effect = create_effect(BLOCK_EFFECT | flags2, fn, true); + return effect; +} +function branch(fn) { + return create_effect(BRANCH_EFFECT | EFFECT_PRESERVED, fn, true); +} +function execute_effect_teardown(effect) { + var teardown = effect.teardown; + if (teardown !== null) { + const previously_destroying_effect = is_destroying_effect; + const previous_reaction = active_reaction; + set_is_destroying_effect(true); + set_active_reaction(null); + try { + teardown.call(null); + } finally { + set_is_destroying_effect(previously_destroying_effect); + set_active_reaction(previous_reaction); + } + } +} +function destroy_effect_children(signal, remove_dom = false) { + var effect = signal.first; + signal.first = signal.last = null; + while (effect !== null) { + const controller = effect.ac; + if (controller !== null) { + without_reactive_context(() => { + controller.abort(STALE_REACTION); + }); + } + var next2 = effect.next; + if ((effect.f & ROOT_EFFECT) !== 0) { + effect.parent = null; + } else { + destroy_effect(effect, remove_dom); + } + effect = next2; + } +} +function destroy_block_effect_children(signal) { + var effect = signal.first; + while (effect !== null) { + var next2 = effect.next; + if ((effect.f & BRANCH_EFFECT) === 0) { + destroy_effect(effect); + } + effect = next2; + } +} +function destroy_effect(effect, remove_dom = true) { + var removed = false; + if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes !== null && effect.nodes.end !== null) { + remove_effect_dom( + effect.nodes.start, + /** @type {TemplateNode} */ + effect.nodes.end + ); + removed = true; + } + destroy_effect_children(effect, remove_dom && !removed); + remove_reactions(effect, 0); + set_signal_status(effect, DESTROYED); + var transitions = effect.nodes && effect.nodes.t; + if (transitions !== null) { + for (const transition of transitions) { + transition.stop(); + } + } + execute_effect_teardown(effect); + var parent = effect.parent; + if (parent !== null && parent.first !== null) { + unlink_effect(effect); + } + effect.next = effect.prev = effect.teardown = effect.ctx = effect.deps = effect.fn = effect.nodes = effect.ac = null; +} +function remove_effect_dom(node, end) { + while (node !== null) { + var next2 = node === end ? null : /* @__PURE__ */ get_next_sibling(node); + node.remove(); + node = next2; + } +} +function unlink_effect(effect) { + var parent = effect.parent; + var prev = effect.prev; + var next2 = effect.next; + if (prev !== null) prev.next = next2; + if (next2 !== null) next2.prev = prev; + if (parent !== null) { + if (parent.first === effect) parent.first = next2; + if (parent.last === effect) parent.last = prev; + } +} +function pause_effect(effect, callback, destroy = true) { + var transitions = []; + pause_children(effect, transitions, true); + var fn = () => { + if (destroy) destroy_effect(effect); + if (callback) callback(); + }; + var remaining = transitions.length; + if (remaining > 0) { + var check = () => --remaining || fn(); + for (var transition of transitions) { + transition.out(check); + } + } else { + fn(); + } +} +function pause_children(effect, transitions, local) { + if ((effect.f & INERT) !== 0) return; + effect.f ^= INERT; + var t = effect.nodes && effect.nodes.t; + if (t !== null) { + for (const transition of t) { + if (transition.is_global || local) { + transitions.push(transition); + } + } + } + var child = effect.first; + while (child !== null) { + var sibling = child.next; + var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || // If this is a branch effect without a block effect parent, + // it means the parent block effect was pruned. In that case, + // transparency information was transferred to the branch effect. + (child.f & BRANCH_EFFECT) !== 0 && (effect.f & BLOCK_EFFECT) !== 0; + pause_children(child, transitions, transparent ? local : false); + child = sibling; + } +} +function move_effect(effect, fragment) { + if (!effect.nodes) return; + var node = effect.nodes.start; + var end = effect.nodes.end; + while (node !== null) { + var next2 = node === end ? null : /* @__PURE__ */ get_next_sibling(node); + fragment.append(node); + node = next2; + } +} +let is_updating_effect = false; +let is_destroying_effect = false; +function set_is_destroying_effect(value) { + is_destroying_effect = value; +} +let active_reaction = null; +let untracking = false; +function set_active_reaction(reaction) { + active_reaction = reaction; +} +let active_effect = null; +function set_active_effect(effect) { + active_effect = effect; +} +let current_sources = null; +function push_reaction_value(value) { + if (active_reaction !== null && true) { + if (current_sources === null) { + current_sources = [value]; + } else { + current_sources.push(value); + } + } +} +let new_deps = null; +let skipped_deps = 0; +let untracked_writes = null; +function set_untracked_writes(value) { + untracked_writes = value; +} +let write_version = 1; +let read_version = 0; +let update_version = read_version; +function set_update_version(value) { + update_version = value; +} +function increment_write_version() { + return ++write_version; +} +function is_dirty(reaction) { + var flags2 = reaction.f; + if ((flags2 & DIRTY) !== 0) { + return true; + } + if (flags2 & DERIVED) { + reaction.f &= ~WAS_MARKED; + } + if ((flags2 & MAYBE_DIRTY) !== 0) { + var dependencies = ( + /** @type {Value[]} */ + reaction.deps + ); + var length = dependencies.length; + for (var i = 0; i < length; i++) { + var dependency = dependencies[i]; + if (is_dirty( + /** @type {Derived} */ + dependency + )) { + update_derived( + /** @type {Derived} */ + dependency + ); + } + if (dependency.wv > reaction.wv) { + return true; + } + } + if ((flags2 & CONNECTED) !== 0 && // During time traveling we don't want to reset the status so that + // traversal of the graph in the other batches still happens + batch_values === null) { + set_signal_status(reaction, CLEAN); + } + } + return false; +} +function schedule_possible_effect_self_invalidation(signal, effect, root2 = true) { + var reactions = signal.reactions; + if (reactions === null) return; + if (current_sources !== null && includes.call(current_sources, signal)) { + return; + } + for (var i = 0; i < reactions.length; i++) { + var reaction = reactions[i]; + if ((reaction.f & DERIVED) !== 0) { + schedule_possible_effect_self_invalidation( + /** @type {Derived} */ + reaction, + effect, + false + ); + } else if (effect === reaction) { + if (root2) { + set_signal_status(reaction, DIRTY); + } else if ((reaction.f & CLEAN) !== 0) { + set_signal_status(reaction, MAYBE_DIRTY); + } + schedule_effect( + /** @type {Effect} */ + reaction + ); + } + } +} +function update_reaction(reaction) { + var previous_deps = new_deps; + var previous_skipped_deps = skipped_deps; + var previous_untracked_writes = untracked_writes; + var previous_reaction = active_reaction; + var previous_sources = current_sources; + var previous_component_context = component_context; + var previous_untracking = untracking; + var previous_update_version = update_version; + var flags2 = reaction.f; + new_deps = /** @type {null | Value[]} */ + null; + skipped_deps = 0; + untracked_writes = null; + active_reaction = (flags2 & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; + current_sources = null; + set_component_context(reaction.ctx); + untracking = false; + update_version = ++read_version; + if (reaction.ac !== null) { + without_reactive_context(() => { + reaction.ac.abort(STALE_REACTION); + }); + reaction.ac = null; + } + try { + reaction.f |= REACTION_IS_UPDATING; + var fn = ( + /** @type {Function} */ + reaction.fn + ); + var result = fn(); + reaction.f |= REACTION_RAN; + var deps = reaction.deps; + var is_fork = current_batch?.is_fork; + if (new_deps !== null) { + var i; + if (!is_fork) { + remove_reactions(reaction, skipped_deps); + } + if (deps !== null && skipped_deps > 0) { + deps.length = skipped_deps + new_deps.length; + for (i = 0; i < new_deps.length; i++) { + deps[skipped_deps + i] = new_deps[i]; + } + } else { + reaction.deps = deps = new_deps; + } + if (effect_tracking() && (reaction.f & CONNECTED) !== 0) { + for (i = skipped_deps; i < deps.length; i++) { + (deps[i].reactions ??= []).push(reaction); + } + } + } else if (!is_fork && deps !== null && skipped_deps < deps.length) { + remove_reactions(reaction, skipped_deps); + deps.length = skipped_deps; + } + if (is_runes() && untracked_writes !== null && !untracking && deps !== null && (reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0) { + for (i = 0; i < /** @type {Source[]} */ + untracked_writes.length; i++) { + schedule_possible_effect_self_invalidation( + untracked_writes[i], + /** @type {Effect} */ + reaction + ); + } + } + if (previous_reaction !== null && previous_reaction !== reaction) { + read_version++; + if (previous_reaction.deps !== null) { + for (let i2 = 0; i2 < previous_skipped_deps; i2 += 1) { + previous_reaction.deps[i2].rv = read_version; + } + } + if (previous_deps !== null) { + for (const dep of previous_deps) { + dep.rv = read_version; + } + } + if (untracked_writes !== null) { + if (previous_untracked_writes === null) { + previous_untracked_writes = untracked_writes; + } else { + previous_untracked_writes.push(.../** @type {Source[]} */ + untracked_writes); + } + } + } + if ((reaction.f & ERROR_VALUE) !== 0) { + reaction.f ^= ERROR_VALUE; + } + return result; + } catch (error) { + return handle_error(error); + } finally { + reaction.f ^= REACTION_IS_UPDATING; + new_deps = previous_deps; + skipped_deps = previous_skipped_deps; + untracked_writes = previous_untracked_writes; + active_reaction = previous_reaction; + current_sources = previous_sources; + set_component_context(previous_component_context); + untracking = previous_untracking; + update_version = previous_update_version; + } +} +function remove_reaction(signal, dependency) { + let reactions = dependency.reactions; + if (reactions !== null) { + var index = index_of.call(reactions, signal); + if (index !== -1) { + var new_length = reactions.length - 1; + if (new_length === 0) { + reactions = dependency.reactions = null; + } else { + reactions[index] = reactions[new_length]; + reactions.pop(); + } + } + } + if (reactions === null && (dependency.f & DERIVED) !== 0 && // Destroying a child effect while updating a parent effect can cause a dependency to appear + // to be unused, when in fact it is used by the currently-updating parent. Checking `new_deps` + // allows us to skip the expensive work of disconnecting and immediately reconnecting it + (new_deps === null || !includes.call(new_deps, dependency))) { + var derived2 = ( + /** @type {Derived} */ + dependency + ); + if ((derived2.f & CONNECTED) !== 0) { + derived2.f ^= CONNECTED; + derived2.f &= ~WAS_MARKED; + } + update_derived_status(derived2); + freeze_derived_effects(derived2); + remove_reactions(derived2, 0); + } +} +function remove_reactions(signal, start_index) { + var dependencies = signal.deps; + if (dependencies === null) return; + for (var i = start_index; i < dependencies.length; i++) { + remove_reaction(signal, dependencies[i]); + } +} +function update_effect(effect) { + var flags2 = effect.f; + if ((flags2 & DESTROYED) !== 0) { + return; + } + set_signal_status(effect, CLEAN); + var previous_effect = active_effect; + var was_updating_effect = is_updating_effect; + active_effect = effect; + is_updating_effect = true; + try { + if ((flags2 & (BLOCK_EFFECT | MANAGED_EFFECT)) !== 0) { + destroy_block_effect_children(effect); + } else { + destroy_effect_children(effect); + } + execute_effect_teardown(effect); + var teardown = update_reaction(effect); + effect.teardown = typeof teardown === "function" ? teardown : null; + effect.wv = write_version; + var dep; + if (DEV && tracing_mode_flag && (effect.f & DIRTY) !== 0 && effect.deps !== null) ; + } finally { + is_updating_effect = was_updating_effect; + active_effect = previous_effect; + } +} +function get(signal) { + var flags2 = signal.f; + var is_derived = (flags2 & DERIVED) !== 0; + if (active_reaction !== null && !untracking) { + var destroyed = active_effect !== null && (active_effect.f & DESTROYED) !== 0; + if (!destroyed && (current_sources === null || !includes.call(current_sources, signal))) { + var deps = active_reaction.deps; + if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) { + if (signal.rv < read_version) { + signal.rv = read_version; + if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { + skipped_deps++; + } else if (new_deps === null) { + new_deps = [signal]; + } else { + new_deps.push(signal); + } + } + } else { + (active_reaction.deps ??= []).push(signal); + var reactions = signal.reactions; + if (reactions === null) { + signal.reactions = [active_reaction]; + } else if (!includes.call(reactions, active_reaction)) { + reactions.push(active_reaction); + } + } + } + } + if (is_destroying_effect && old_values.has(signal)) { + return old_values.get(signal); + } + if (is_derived) { + var derived2 = ( + /** @type {Derived} */ + signal + ); + if (is_destroying_effect) { + var value = derived2.v; + if ((derived2.f & CLEAN) === 0 && derived2.reactions !== null || depends_on_old_values(derived2)) { + value = execute_derived(derived2); + } + old_values.set(derived2, value); + return value; + } + var should_connect = (derived2.f & CONNECTED) === 0 && !untracking && active_reaction !== null && (is_updating_effect || (active_reaction.f & CONNECTED) !== 0); + var is_new = (derived2.f & REACTION_RAN) === 0; + if (is_dirty(derived2)) { + if (should_connect) { + derived2.f |= CONNECTED; + } + update_derived(derived2); + } + if (should_connect && !is_new) { + unfreeze_derived_effects(derived2); + reconnect(derived2); + } + } + if (batch_values?.has(signal)) { + return batch_values.get(signal); + } + if ((signal.f & ERROR_VALUE) !== 0) { + throw signal.v; + } + return signal.v; +} +function reconnect(derived2) { + derived2.f |= CONNECTED; + if (derived2.deps === null) return; + for (const dep of derived2.deps) { + (dep.reactions ??= []).push(derived2); + if ((dep.f & DERIVED) !== 0 && (dep.f & CONNECTED) === 0) { + unfreeze_derived_effects( + /** @type {Derived} */ + dep + ); + reconnect( + /** @type {Derived} */ + dep + ); + } + } +} +function depends_on_old_values(derived2) { + if (derived2.v === UNINITIALIZED) return true; + if (derived2.deps === null) return false; + for (const dep of derived2.deps) { + if (old_values.has(dep)) { + return true; + } + if ((dep.f & DERIVED) !== 0 && depends_on_old_values( + /** @type {Derived} */ + dep + )) { + return true; + } + } + return false; +} +function untrack(fn) { + var previous_untracking = untracking; + try { + untracking = true; + return fn(); + } finally { + untracking = previous_untracking; + } +} +const event_symbol = /* @__PURE__ */ Symbol("events"); +const all_registered_events = /* @__PURE__ */ new Set(); +const root_event_handles = /* @__PURE__ */ new Set(); +let last_propagated_event = null; +function handle_event_propagation(event) { + var handler_element = this; + var owner_document = ( + /** @type {Node} */ + handler_element.ownerDocument + ); + var event_name = event.type; + var path = event.composedPath?.() || []; + var current_target = ( + /** @type {null | Element} */ + path[0] || event.target + ); + last_propagated_event = event; + var path_idx = 0; + var handled_at = last_propagated_event === event && event[event_symbol]; + if (handled_at) { + var at_idx = path.indexOf(handled_at); + if (at_idx !== -1 && (handler_element === document || handler_element === /** @type {any} */ + window)) { + event[event_symbol] = handler_element; + return; + } + var handler_idx = path.indexOf(handler_element); + if (handler_idx === -1) { + return; + } + if (at_idx <= handler_idx) { + path_idx = at_idx; + } + } + current_target = /** @type {Element} */ + path[path_idx] || event.target; + if (current_target === handler_element) return; + define_property(event, "currentTarget", { + configurable: true, + get() { + return current_target || owner_document; + } + }); + var previous_reaction = active_reaction; + var previous_effect = active_effect; + set_active_reaction(null); + set_active_effect(null); + try { + var throw_error; + var other_errors = []; + while (current_target !== null) { + var parent_element = current_target.assignedSlot || current_target.parentNode || /** @type {any} */ + current_target.host || null; + try { + var delegated = current_target[event_symbol]?.[event_name]; + if (delegated != null && (!/** @type {any} */ + current_target.disabled || // DOM could've been updated already by the time this is reached, so we check this as well + // -> the target could not have been disabled because it emits the event in the first place + event.target === current_target)) { + delegated.call(current_target, event); + } + } catch (error) { + if (throw_error) { + other_errors.push(error); + } else { + throw_error = error; + } + } + if (event.cancelBubble || parent_element === handler_element || parent_element === null) { + break; + } + current_target = parent_element; + } + if (throw_error) { + for (let error of other_errors) { + queueMicrotask(() => { + throw error; + }); + } + throw throw_error; + } + } finally { + event[event_symbol] = handler_element; + delete event.currentTarget; + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); + } +} +function assign_nodes(start, end) { + var effect = ( + /** @type {Effect} */ + active_effect + ); + if (effect.nodes === null) { + effect.nodes = { start, end, a: null, t: null }; + } +} +function mount(component, options) { + return _mount(component, options); +} +function hydrate(component, options) { + init_operations(); + options.intro = options.intro ?? false; + const target = options.target; + const was_hydrating = hydrating; + const previous_hydrate_node = hydrate_node; + try { + var anchor = /* @__PURE__ */ get_first_child(target); + while (anchor && (anchor.nodeType !== COMMENT_NODE || /** @type {Comment} */ + anchor.data !== HYDRATION_START)) { + anchor = /* @__PURE__ */ get_next_sibling(anchor); + } + if (!anchor) { + throw HYDRATION_ERROR; + } + set_hydrating(true); + set_hydrate_node( + /** @type {Comment} */ + anchor + ); + const instance = _mount(component, { ...options, anchor }); + set_hydrating(false); + return ( + /** @type {Exports} */ + instance + ); + } catch (error) { + if (error instanceof Error && error.message.split("\n").some((line) => line.startsWith("https://svelte.dev/e/"))) { + throw error; + } + if (error !== HYDRATION_ERROR) { + console.warn("Failed to hydrate: ", error); + } + if (options.recover === false) { + hydration_failed(); + } + init_operations(); + clear_text_content(target); + set_hydrating(false); + return mount(component, options); + } finally { + set_hydrating(was_hydrating); + set_hydrate_node(previous_hydrate_node); + } +} +const listeners = /* @__PURE__ */ new Map(); +function _mount(Component, { target, anchor, props = {}, events, context, intro = true, transformError }) { + init_operations(); + var component = void 0; + var unmount2 = component_root(() => { + var anchor_node = anchor ?? target.appendChild(create_text()); + boundary( + /** @type {TemplateNode} */ + anchor_node, + { + pending: () => { + } + }, + (anchor_node2) => { + push({}); + var ctx = ( + /** @type {ComponentContext} */ + component_context + ); + if (context) ctx.c = context; + if (events) { + props.$$events = events; + } + if (hydrating) { + assign_nodes( + /** @type {TemplateNode} */ + anchor_node2, + null + ); + } + component = Component(anchor_node2, props) || {}; + if (hydrating) { + active_effect.nodes.end = hydrate_node; + if (hydrate_node === null || hydrate_node.nodeType !== COMMENT_NODE || /** @type {Comment} */ + hydrate_node.data !== HYDRATION_END) { + hydration_mismatch(); + throw HYDRATION_ERROR; + } + } + pop(); + }, + transformError + ); + var registered_events = /* @__PURE__ */ new Set(); + var event_handle = (events2) => { + for (var i = 0; i < events2.length; i++) { + var event_name = events2[i]; + if (registered_events.has(event_name)) continue; + registered_events.add(event_name); + var passive = is_passive_event(event_name); + for (const node of [target, document]) { + var counts = listeners.get(node); + if (counts === void 0) { + counts = /* @__PURE__ */ new Map(); + listeners.set(node, counts); + } + var count = counts.get(event_name); + if (count === void 0) { + node.addEventListener(event_name, handle_event_propagation, { passive }); + counts.set(event_name, 1); + } else { + counts.set(event_name, count + 1); + } + } + } + }; + event_handle(array_from(all_registered_events)); + root_event_handles.add(event_handle); + return () => { + for (var event_name of registered_events) { + for (const node of [target, document]) { + var counts = ( + /** @type {Map} */ + listeners.get(node) + ); + var count = ( + /** @type {number} */ + counts.get(event_name) + ); + if (--count == 0) { + node.removeEventListener(event_name, handle_event_propagation); + counts.delete(event_name); + if (counts.size === 0) { + listeners.delete(node); + } + } else { + counts.set(event_name, count); + } + } + } + root_event_handles.delete(event_handle); + if (anchor_node !== anchor) { + anchor_node.parentNode?.removeChild(anchor_node); + } + }; + }); + mounted_components.set(component, unmount2); + return component; +} +let mounted_components = /* @__PURE__ */ new WeakMap(); +function unmount(component, options) { + const fn = mounted_components.get(component); + if (fn) { + mounted_components.delete(component); + return fn(options); + } + return Promise.resolve(); +} +function asClassComponent$1(component) { + return class extends Svelte4Component { + /** @param {any} options */ + constructor(options) { + super({ + component, + ...options + }); + } + }; +} +class Svelte4Component { + /** @type {any} */ + #events; + /** @type {Record} */ + #instance; + /** + * @param {ComponentConstructorOptions & { + * component: any; + * }} options + */ + constructor(options) { + var sources = /* @__PURE__ */ new Map(); + var add_source = (key, value) => { + var s = /* @__PURE__ */ mutable_source(value, false, false); + sources.set(key, s); + return s; + }; + const props = new Proxy( + { ...options.props || {}, $$events: {} }, + { + get(target, prop) { + return get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); + }, + has(target, prop) { + if (prop === LEGACY_PROPS) return true; + get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); + return Reflect.has(target, prop); + }, + set(target, prop, value) { + set(sources.get(prop) ?? add_source(prop, value), value); + return Reflect.set(target, prop, value); + } + } + ); + this.#instance = (options.hydrate ? hydrate : mount)(options.component, { + target: options.target, + anchor: options.anchor, + props, + context: options.context, + intro: options.intro ?? false, + recover: options.recover, + transformError: options.transformError + }); + if (!options?.props?.$$host || options.sync === false) { + flushSync(); + } + this.#events = props.$$events; + for (const key of Object.keys(this.#instance)) { + if (key === "$set" || key === "$destroy" || key === "$on") continue; + define_property(this, key, { + get() { + return this.#instance[key]; + }, + /** @param {any} value */ + set(value) { + this.#instance[key] = value; + }, + enumerable: true + }); + } + this.#instance.$set = /** @param {Record} next */ + (next2) => { + Object.assign(props, next2); + }; + this.#instance.$destroy = () => { + unmount(this.#instance); + }; + } + /** @param {Record} props */ + $set(props) { + this.#instance.$set(props); + } + /** + * @param {string} event + * @param {(...args: any[]) => any} callback + * @returns {any} + */ + $on(event, callback) { + this.#events[event] = this.#events[event] || []; + const cb = (...args) => callback.call(this, ...args); + this.#events[event].push(cb); + return () => { + this.#events[event] = this.#events[event].filter( + /** @param {any} fn */ + (fn) => fn !== cb + ); + }; + } + $destroy() { + this.#instance.$destroy(); + } +} +function asClassComponent(component) { + const component_constructor = asClassComponent$1(component); + const _render = (props, { context, csp, transformError } = {}) => { + const result = render(component, { props, context, csp, transformError }); + const munged = Object.defineProperties( + /** @type {LegacyRenderResult & PromiseLike} */ + {}, + { + css: { + value: { code: "", map: null } + }, + head: { + get: () => result.head + }, + html: { + get: () => result.body + }, + then: { + /** + * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function. + * + * @template TResult1 + * @template [TResult2=never] + * @param { (value: LegacyRenderResult) => TResult1 } onfulfilled + * @param { (reason: unknown) => TResult2 } onrejected + */ + value: (onfulfilled, onrejected) => { + { + const user_result = onfulfilled({ + css: munged.css, + head: munged.head, + html: munged.html + }); + return Promise.resolve(user_result); + } + } + } + } + ); + return munged; + }; + component_constructor.render = _render; + return component_constructor; +} +function Root($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { + stores, + page, + constructors, + components = [], + form, + data_0 = null, + data_1 = null, + data_2 = null + } = $$props; + { + setContext("__svelte__", stores); + } + { + stores.page.set(page); + } + const Pyramid_2 = derived(() => constructors[2]); + if (constructors[1]) { + $$renderer2.push(""); + const Pyramid_0 = constructors[0]; + if (Pyramid_0) { + $$renderer2.push(""); + Pyramid_0($$renderer2, { + data: data_0, + form, + params: page.params, + children: ($$renderer3) => { + if (constructors[2]) { + $$renderer3.push(""); + const Pyramid_1 = constructors[1]; + if (Pyramid_1) { + $$renderer3.push(""); + Pyramid_1($$renderer3, { + data: data_1, + form, + params: page.params, + children: ($$renderer4) => { + if (Pyramid_2()) { + $$renderer4.push(""); + Pyramid_2()($$renderer4, { data: data_2, form, params: page.params }); + $$renderer4.push(""); + } else { + $$renderer4.push(""); + $$renderer4.push(""); + } + }, + $$slots: { default: true } + }); + $$renderer3.push(""); + } else { + $$renderer3.push(""); + $$renderer3.push(""); + } + } else { + $$renderer3.push(""); + const Pyramid_1 = constructors[1]; + if (Pyramid_1) { + $$renderer3.push(""); + Pyramid_1($$renderer3, { data: data_1, form, params: page.params }); + $$renderer3.push(""); + } else { + $$renderer3.push(""); + $$renderer3.push(""); + } + } + $$renderer3.push(``); + }, + $$slots: { default: true } + }); + $$renderer2.push(""); + } else { + $$renderer2.push(""); + $$renderer2.push(""); + } + } else { + $$renderer2.push(""); + const Pyramid_0 = constructors[0]; + if (Pyramid_0) { + $$renderer2.push(""); + Pyramid_0($$renderer2, { data: data_0, form, params: page.params }); + $$renderer2.push(""); + } else { + $$renderer2.push(""); + $$renderer2.push(""); + } + } + $$renderer2.push(` `); + { + $$renderer2.push(""); + } + $$renderer2.push(``); + }); +} +const root = asClassComponent(Root); +export { + root as r, + safe_not_equal as s +}; diff --git a/.svelte-kit/output/server/chunks/routing.js b/.svelte-kit/output/server/chunks/routing.js new file mode 100644 index 0000000..11c0ffa --- /dev/null +++ b/.svelte-kit/output/server/chunks/routing.js @@ -0,0 +1,85 @@ +import { b as decode_params } from "./url.js"; +function affects_path(segment) { + return segment !== "" && !/^\([^)]+\)$/.test(segment); +} +function get_route_segments(route) { + return route.slice(1).split("/").filter(affects_path); +} +function exec(match, params, matchers) { + const result = {}; + const values = match.slice(1); + const values_needing_match = values.filter((value) => value !== void 0); + let buffered = 0; + for (let i = 0; i < params.length; i += 1) { + const param = params[i]; + let value = values[i - buffered]; + if (param.chained && param.rest && buffered) { + value = values.slice(i - buffered, i + 1).filter((s) => s).join("/"); + buffered = 0; + } + if (value === void 0) { + if (param.rest) { + value = ""; + } else { + continue; + } + } + if (!param.matcher || matchers[param.matcher](value)) { + result[param.name] = value; + const next_param = params[i + 1]; + const next_value = values[i + 1]; + if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) { + buffered = 0; + } + if (!next_param && !next_value && Object.keys(result).length === values_needing_match.length) { + buffered = 0; + } + continue; + } + if (param.optional && param.chained) { + buffered++; + continue; + } + return; + } + if (buffered) return; + return result; +} +const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g; +function resolve_route(id, params) { + const segments = get_route_segments(id); + const has_id_trailing_slash = id.endsWith("/"); + return "/" + segments.map( + (segment) => segment.replace(basic_param_pattern, (_, optional, rest, name) => { + const param_value = params[name]; + if (!param_value) { + if (optional) return ""; + if (rest && param_value !== void 0) return ""; + throw new Error(`Missing parameter '${name}' in route ${id}`); + } + if (param_value.startsWith("/") || param_value.endsWith("/")) + throw new Error( + `Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar` + ); + return param_value; + }) + ).filter(Boolean).join("/") + (has_id_trailing_slash ? "/" : ""); +} +function find_route(path, routes, matchers) { + for (const route of routes) { + const match = route.pattern.exec(path); + if (!match) continue; + const matched = exec(match, route.params, matchers); + if (matched) { + return { + route, + params: decode_params(matched) + }; + } + } + return null; +} +export { + find_route as f, + resolve_route as r +}; diff --git a/.svelte-kit/output/server/chunks/server.js b/.svelte-kit/output/server/chunks/server.js new file mode 100644 index 0000000..bf7eaa6 --- /dev/null +++ b/.svelte-kit/output/server/chunks/server.js @@ -0,0 +1,27 @@ +let base = ""; +let assets = base; +const app_dir = "_app"; +const relative = true; +const initial = { base, assets }; +const initial_base = initial.base; +function override(paths) { + base = paths.base; + assets = paths.assets; +} +function reset() { + base = initial.base; + assets = initial.assets; +} +function set_assets(path) { + assets = initial.assets = path; +} +export { + assets as a, + base as b, + app_dir as c, + reset as d, + initial_base as i, + override as o, + relative as r, + set_assets as s +}; diff --git a/.svelte-kit/output/server/chunks/shared-server.js b/.svelte-kit/output/server/chunks/shared-server.js new file mode 100644 index 0000000..4fa13e6 --- /dev/null +++ b/.svelte-kit/output/server/chunks/shared-server.js @@ -0,0 +1,14 @@ +let private_env = {}; +let public_env = {}; +function set_private_env(environment) { + private_env = environment; +} +function set_public_env(environment) { + public_env = environment; +} +export { + set_public_env as a, + private_env as b, + public_env as p, + set_private_env as s +}; diff --git a/.svelte-kit/output/server/chunks/shared.js b/.svelte-kit/output/server/chunks/shared.js new file mode 100644 index 0000000..9162124 --- /dev/null +++ b/.svelte-kit/output/server/chunks/shared.js @@ -0,0 +1,789 @@ +import { json, text } from "@sveltejs/kit"; +import { SvelteKitError, HttpError } from "@sveltejs/kit/internal"; +import { with_request_store } from "@sveltejs/kit/internal/server"; +import * as devalue from "devalue"; +import { t as text_decoder, b as base64_encode, c as base64_decode } from "./utils.js"; +const SVELTE_KIT_ASSETS = "/_svelte_kit_assets"; +const ENDPOINT_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]; +const MUTATIVE_METHODS = ["POST", "PUT", "PATCH", "DELETE"]; +const PAGE_METHODS = ["GET", "POST", "HEAD"]; +function set_nested_value(object, path_string, value) { + if (path_string.startsWith("n:")) { + path_string = path_string.slice(2); + value = value === "" ? void 0 : parseFloat(value); + } else if (path_string.startsWith("b:")) { + path_string = path_string.slice(2); + value = value === "on"; + } + deep_set(object, split_path(path_string), value); +} +function convert_formdata(data) { + const result = {}; + for (let key of data.keys()) { + const is_array = key.endsWith("[]"); + let values = data.getAll(key); + if (is_array) key = key.slice(0, -2); + if (values.length > 1 && !is_array) { + throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`); + } + values = values.filter( + (entry) => typeof entry === "string" || entry.name !== "" || entry.size > 0 + ); + if (key.startsWith("n:")) { + key = key.slice(2); + values = values.map((v) => v === "" ? void 0 : parseFloat( + /** @type {string} */ + v + )); + } else if (key.startsWith("b:")) { + key = key.slice(2); + values = values.map((v) => v === "on"); + } + set_nested_value(result, key, is_array ? values : values[0]); + } + return result; +} +const BINARY_FORM_CONTENT_TYPE = "application/x-sveltekit-formdata"; +const BINARY_FORM_VERSION = 0; +const HEADER_BYTES = 1 + 4 + 2; +async function deserialize_binary_form(request) { + if (request.headers.get("content-type") !== BINARY_FORM_CONTENT_TYPE) { + const form_data = await request.formData(); + return { data: convert_formdata(form_data), meta: {}, form_data }; + } + if (!request.body) { + throw deserialize_error("no body"); + } + const content_length = parseInt(request.headers.get("content-length") ?? ""); + if (Number.isNaN(content_length)) { + throw deserialize_error("invalid Content-Length header"); + } + const reader = request.body.getReader(); + const chunks = []; + function get_chunk(index) { + if (index in chunks) return chunks[index]; + let i = chunks.length; + while (i <= index) { + chunks[i] = reader.read().then((chunk) => chunk.value); + i++; + } + return chunks[index]; + } + async function get_buffer(offset, length) { + let start_chunk; + let chunk_start = 0; + let chunk_index; + for (chunk_index = 0; ; chunk_index++) { + const chunk = await get_chunk(chunk_index); + if (!chunk) return null; + const chunk_end = chunk_start + chunk.byteLength; + if (offset >= chunk_start && offset < chunk_end) { + start_chunk = chunk; + break; + } + chunk_start = chunk_end; + } + if (offset + length <= chunk_start + start_chunk.byteLength) { + return start_chunk.subarray(offset - chunk_start, offset + length - chunk_start); + } + const chunks2 = [start_chunk.subarray(offset - chunk_start)]; + let cursor = start_chunk.byteLength - offset + chunk_start; + while (cursor < length) { + chunk_index++; + let chunk = await get_chunk(chunk_index); + if (!chunk) return null; + if (chunk.byteLength > length - cursor) { + chunk = chunk.subarray(0, length - cursor); + } + chunks2.push(chunk); + cursor += chunk.byteLength; + } + const buffer = new Uint8Array(length); + cursor = 0; + for (const chunk of chunks2) { + buffer.set(chunk, cursor); + cursor += chunk.byteLength; + } + return buffer; + } + const header = await get_buffer(0, HEADER_BYTES); + if (!header) throw deserialize_error("too short"); + if (header[0] !== BINARY_FORM_VERSION) { + throw deserialize_error(`got version ${header[0]}, expected version ${BINARY_FORM_VERSION}`); + } + const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength); + const data_length = header_view.getUint32(1, true); + if (HEADER_BYTES + data_length > content_length) { + throw deserialize_error("data overflow"); + } + const file_offsets_length = header_view.getUint16(5, true); + if (HEADER_BYTES + data_length + file_offsets_length > content_length) { + throw deserialize_error("file offset table overflow"); + } + const data_buffer = await get_buffer(HEADER_BYTES, data_length); + if (!data_buffer) throw deserialize_error("data too short"); + let file_offsets; + let files_start_offset; + if (file_offsets_length > 0) { + const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length); + if (!file_offsets_buffer) throw deserialize_error("file offset table too short"); + const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer)); + if (!Array.isArray(parsed_offsets) || parsed_offsets.some((n) => typeof n !== "number" || !Number.isInteger(n) || n < 0)) { + throw deserialize_error("invalid file offset table"); + } + file_offsets = /** @type {Array} */ + parsed_offsets; + files_start_offset = HEADER_BYTES + data_length + file_offsets_length; + } + const file_spans = []; + const [data, meta] = devalue.parse(text_decoder.decode(data_buffer), { + File: ([name, type, size, last_modified, index]) => { + if (typeof name !== "string" || typeof type !== "string" || typeof size !== "number" || typeof last_modified !== "number" || typeof index !== "number") { + throw deserialize_error("invalid file metadata"); + } + let offset = file_offsets[index]; + if (offset === void 0) { + throw deserialize_error("duplicate file offset table index"); + } + file_offsets[index] = void 0; + offset += files_start_offset; + if (offset + size > content_length) { + throw deserialize_error("file data overflow"); + } + file_spans.push({ offset, size }); + return new Proxy(new LazyFile(name, type, size, last_modified, get_chunk, offset), { + getPrototypeOf() { + return File.prototype; + } + }); + } + }); + file_spans.sort((a, b) => a.offset - b.offset || a.size - b.size); + for (let i = 1; i < file_spans.length; i++) { + const previous = file_spans[i - 1]; + const current = file_spans[i]; + const previous_end = previous.offset + previous.size; + if (previous_end < current.offset) { + throw deserialize_error("gaps in file data"); + } + if (previous_end > current.offset) { + throw deserialize_error("overlapping file data"); + } + } + void (async () => { + let has_more = true; + while (has_more) { + const chunk = await get_chunk(chunks.length); + has_more = !!chunk; + } + })(); + return { data, meta, form_data: null }; +} +function deserialize_error(message) { + return new SvelteKitError(400, "Bad Request", `Could not deserialize binary form: ${message}`); +} +class LazyFile { + /** @type {(index: number) => Promise | undefined>} */ + #get_chunk; + /** @type {number} */ + #offset; + /** + * @param {string} name + * @param {string} type + * @param {number} size + * @param {number} last_modified + * @param {(index: number) => Promise | undefined>} get_chunk + * @param {number} offset + */ + constructor(name, type, size, last_modified, get_chunk, offset) { + this.name = name; + this.type = type; + this.size = size; + this.lastModified = last_modified; + this.webkitRelativePath = ""; + this.#get_chunk = get_chunk; + this.#offset = offset; + this.arrayBuffer = this.arrayBuffer.bind(this); + this.bytes = this.bytes.bind(this); + this.slice = this.slice.bind(this); + this.stream = this.stream.bind(this); + this.text = this.text.bind(this); + } + /** @type {ArrayBuffer | undefined} */ + #buffer; + async arrayBuffer() { + this.#buffer ??= await new Response(this.stream()).arrayBuffer(); + return this.#buffer; + } + async bytes() { + return new Uint8Array(await this.arrayBuffer()); + } + /** + * @param {number=} start + * @param {number=} end + * @param {string=} contentType + */ + slice(start = 0, end = this.size, contentType = this.type) { + if (start < 0) { + start = Math.max(this.size + start, 0); + } else { + start = Math.min(start, this.size); + } + if (end < 0) { + end = Math.max(this.size + end, 0); + } else { + end = Math.min(end, this.size); + } + const size = Math.max(end - start, 0); + const file = new LazyFile( + this.name, + contentType, + size, + this.lastModified, + this.#get_chunk, + this.#offset + start + ); + return file; + } + stream() { + let cursor = 0; + let chunk_index = 0; + return new ReadableStream({ + start: async (controller) => { + let chunk_start = 0; + let start_chunk; + for (chunk_index = 0; ; chunk_index++) { + const chunk = await this.#get_chunk(chunk_index); + if (!chunk) return null; + const chunk_end = chunk_start + chunk.byteLength; + if (this.#offset >= chunk_start && this.#offset < chunk_end) { + start_chunk = chunk; + break; + } + chunk_start = chunk_end; + } + if (this.#offset + this.size <= chunk_start + start_chunk.byteLength) { + controller.enqueue( + start_chunk.subarray(this.#offset - chunk_start, this.#offset + this.size - chunk_start) + ); + controller.close(); + } else { + controller.enqueue(start_chunk.subarray(this.#offset - chunk_start)); + cursor = start_chunk.byteLength - this.#offset + chunk_start; + } + }, + pull: async (controller) => { + chunk_index++; + let chunk = await this.#get_chunk(chunk_index); + if (!chunk) { + controller.error("incomplete file data"); + controller.close(); + return; + } + if (chunk.byteLength > this.size - cursor) { + chunk = chunk.subarray(0, this.size - cursor); + } + controller.enqueue(chunk); + cursor += chunk.byteLength; + if (cursor >= this.size) { + controller.close(); + } + } + }); + } + async text() { + return text_decoder.decode(await this.arrayBuffer()); + } +} +const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/; +function split_path(path) { + if (!path_regex.test(path)) { + throw new Error(`Invalid path ${path}`); + } + return path.split(/\.|\[|\]/).filter(Boolean); +} +function check_prototype_pollution(key) { + if (key === "__proto__" || key === "constructor" || key === "prototype") { + throw new Error( + `Invalid key "${key}"` + ); + } +} +function deep_set(object, keys, value) { + let current = object; + for (let i = 0; i < keys.length - 1; i += 1) { + const key = keys[i]; + check_prototype_pollution(key); + const is_array = /^\d+$/.test(keys[i + 1]); + const exists = Object.hasOwn(current, key); + const inner = current[key]; + if (exists && is_array !== Array.isArray(inner)) { + throw new Error(`Invalid array key ${keys[i + 1]}`); + } + if (!exists) { + current[key] = is_array ? [] : {}; + } + current = current[key]; + } + const final_key = keys[keys.length - 1]; + check_prototype_pollution(final_key); + current[final_key] = value; +} +function normalize_issue(issue, server = false) { + const normalized = { name: "", path: [], message: issue.message, server }; + if (issue.path !== void 0) { + let name = ""; + for (const segment of issue.path) { + const key = ( + /** @type {string | number} */ + typeof segment === "object" ? segment.key : segment + ); + normalized.path.push(key); + if (typeof key === "number") { + name += `[${key}]`; + } else if (typeof key === "string") { + name += name === "" ? key : "." + key; + } + } + normalized.name = name; + } + return normalized; +} +function flatten_issues(issues) { + const result = {}; + for (const issue of issues) { + (result.$ ??= []).push(issue); + let name = ""; + if (issue.path !== void 0) { + for (const key of issue.path) { + if (typeof key === "number") { + name += `[${key}]`; + } else if (typeof key === "string") { + name += name === "" ? key : "." + key; + } + (result[name] ??= []).push(issue); + } + } + } + return result; +} +function deep_get(object, path) { + let current = object; + for (const key of path) { + if (current == null || typeof current !== "object") { + return current; + } + current = current[key]; + } + return current; +} +function create_field_proxy(target, get_input, set_input, get_issues, path = []) { + const get_value = () => { + return deep_get(get_input(), path); + }; + return new Proxy(target, { + get(target2, prop) { + if (typeof prop === "symbol") return target2[prop]; + if (/^\d+$/.test(prop)) { + return create_field_proxy({}, get_input, set_input, get_issues, [ + ...path, + parseInt(prop, 10) + ]); + } + const key = build_path_string(path); + if (prop === "set") { + const set_func = function(newValue) { + set_input(path, newValue); + return newValue; + }; + return create_field_proxy(set_func, get_input, set_input, get_issues, [...path, prop]); + } + if (prop === "value") { + return create_field_proxy(get_value, get_input, set_input, get_issues, [...path, prop]); + } + if (prop === "issues" || prop === "allIssues") { + const issues_func = () => { + const all_issues = get_issues()[key === "" ? "$" : key]; + if (prop === "allIssues") { + return all_issues?.map((issue) => ({ + path: issue.path, + message: issue.message + })); + } + return all_issues?.filter((issue) => issue.name === key)?.map((issue) => ({ + path: issue.path, + message: issue.message + })); + }; + return create_field_proxy(issues_func, get_input, set_input, get_issues, [...path, prop]); + } + if (prop === "as") { + const as_func = (type, input_value) => { + const is_array = type === "file multiple" || type === "select multiple" || type === "checkbox" && typeof input_value === "string"; + const prefix = type === "number" || type === "range" ? "n:" : type === "checkbox" && !is_array ? "b:" : ""; + const base_props = { + name: prefix + key + (is_array ? "[]" : ""), + get "aria-invalid"() { + const issues = get_issues(); + return key in issues ? "true" : void 0; + } + }; + if (type !== "text" && type !== "select" && type !== "select multiple") { + base_props.type = type === "file multiple" ? "file" : type; + } + if (type === "submit" || type === "hidden") { + return Object.defineProperties(base_props, { + value: { value: input_value, enumerable: true } + }); + } + if (type === "select" || type === "select multiple") { + return Object.defineProperties(base_props, { + multiple: { value: is_array, enumerable: true }, + value: { + enumerable: true, + get() { + return get_value(); + } + } + }); + } + if (type === "checkbox" || type === "radio") { + return Object.defineProperties(base_props, { + value: { value: input_value ?? "on", enumerable: true }, + checked: { + enumerable: true, + get() { + const value = get_value(); + if (type === "radio") { + return value === input_value; + } + if (is_array) { + return (value ?? []).includes(input_value); + } + return value; + } + } + }); + } + if (type === "file" || type === "file multiple") { + return Object.defineProperties(base_props, { + multiple: { value: is_array, enumerable: true }, + files: { + enumerable: true, + get() { + const value = get_value(); + if (value instanceof File) { + if (typeof DataTransfer !== "undefined") { + const fileList = new DataTransfer(); + fileList.items.add(value); + return fileList.files; + } + return { 0: value, length: 1 }; + } + if (Array.isArray(value) && value.every((f) => f instanceof File)) { + if (typeof DataTransfer !== "undefined") { + const fileList = new DataTransfer(); + value.forEach((file) => fileList.items.add(file)); + return fileList.files; + } + const fileListLike = { length: value.length }; + value.forEach((file, index) => { + fileListLike[index] = file; + }); + return fileListLike; + } + return null; + } + } + }); + } + return Object.defineProperties(base_props, { + value: { + enumerable: true, + get() { + const value = get_value(); + return value != null ? String(value) : ""; + } + } + }); + }; + return create_field_proxy(as_func, get_input, set_input, get_issues, [...path, "as"]); + } + return create_field_proxy({}, get_input, set_input, get_issues, [...path, prop]); + } + }); +} +function build_path_string(path) { + let result = ""; + for (const segment of path) { + if (typeof segment === "number") { + result += `[${segment}]`; + } else { + result += result === "" ? segment : "." + segment; + } + } + return result; +} +function negotiate(accept, types) { + const parts = []; + accept.split(",").forEach((str, i) => { + const match = /([^/ \t]+)\/([^; \t]+)[ \t]*(?:;[ \t]*q=([0-9.]+))?/.exec(str); + if (match) { + const [, type, subtype, q = "1"] = match; + parts.push({ type, subtype, q: +q, i }); + } + }); + parts.sort((a, b) => { + if (a.q !== b.q) { + return b.q - a.q; + } + if (a.subtype === "*" !== (b.subtype === "*")) { + return a.subtype === "*" ? 1 : -1; + } + if (a.type === "*" !== (b.type === "*")) { + return a.type === "*" ? 1 : -1; + } + return a.i - b.i; + }); + let accepted; + let min_priority = Infinity; + for (const mimetype of types) { + const [type, subtype] = mimetype.split("/"); + const priority = parts.findIndex( + (part) => (part.type === type || part.type === "*") && (part.subtype === subtype || part.subtype === "*") + ); + if (priority !== -1 && priority < min_priority) { + accepted = mimetype; + min_priority = priority; + } + } + return accepted; +} +function is_content_type(request, ...types) { + const type = request.headers.get("content-type")?.split(";", 1)[0].trim() ?? ""; + return types.includes(type.toLowerCase()); +} +function is_form_content_type(request) { + return is_content_type( + request, + "application/x-www-form-urlencoded", + "multipart/form-data", + "text/plain", + BINARY_FORM_CONTENT_TYPE + ); +} +function coalesce_to_error(err) { + return err instanceof Error || err && /** @type {any} */ + err.name && /** @type {any} */ + err.message ? ( + /** @type {Error} */ + err + ) : new Error(JSON.stringify(err)); +} +function normalize_error(error) { + return ( + /** @type {import('../exports/internal/index.js').Redirect | HttpError | SvelteKitError | Error} */ + error + ); +} +function get_status(error) { + return error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500; +} +function get_message(error) { + return error instanceof SvelteKitError ? error.text : "Internal Error"; +} +const escape_html_attr_dict = { + "&": "&", + '"': """ + // Svelte also escapes < because the escape function could be called inside a `noscript` there + // https://github.com/sveltejs/svelte/security/advisories/GHSA-8266-84wp-wv5c + // However, that doesn't apply in SvelteKit +}; +const escape_html_dict = { + "&": "&", + "<": "<" +}; +const surrogates = ( + // high surrogate without paired low surrogate + "[\\ud800-\\udbff](?![\\udc00-\\udfff])|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\udc00-\\udfff]" +); +const escape_html_attr_regex = new RegExp( + `[${Object.keys(escape_html_attr_dict).join("")}]|` + surrogates, + "g" +); +const escape_html_regex = new RegExp( + `[${Object.keys(escape_html_dict).join("")}]|` + surrogates, + "g" +); +function escape_html(str, is_attr) { + const dict = is_attr ? escape_html_attr_dict : escape_html_dict; + const escaped_str = str.replace(is_attr ? escape_html_attr_regex : escape_html_regex, (match) => { + if (match.length === 2) { + return match; + } + return dict[match] ?? `&#${match.charCodeAt(0)};`; + }); + return escaped_str; +} +function method_not_allowed(mod, method) { + return text(`${method} method not allowed`, { + status: 405, + headers: { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: allowed_methods(mod).join(", ") + } + }); +} +function allowed_methods(mod) { + const allowed = ENDPOINT_METHODS.filter((method) => method in mod); + if ("GET" in mod && !("HEAD" in mod)) { + allowed.push("HEAD"); + } + return allowed; +} +function get_global_name(options) { + return `__sveltekit_${options.version_hash}`; +} +function static_error_page(options, status, message) { + let page = options.templates.error({ status, message: escape_html(message) }); + return text(page, { + headers: { "content-type": "text/html; charset=utf-8" }, + status + }); +} +async function handle_fatal_error(event, state, options, error) { + error = error instanceof HttpError ? error : coalesce_to_error(error); + const status = get_status(error); + const body = await handle_error_and_jsonify(event, state, options, error); + const type = negotiate(event.request.headers.get("accept") || "text/html", [ + "application/json", + "text/html" + ]); + if (event.isDataRequest || type === "application/json") { + return json(body, { + status + }); + } + return static_error_page(options, status, body.message); +} +async function handle_error_and_jsonify(event, state, options, error) { + if (error instanceof HttpError) { + return { message: "Unknown Error", ...error.body }; + } + const status = get_status(error); + const message = get_message(error); + return await with_request_store( + { event, state }, + () => options.hooks.handleError({ error, event, status, message }) + ) ?? { message }; +} +function redirect_response(status, location) { + const response = new Response(void 0, { + status, + headers: { location } + }); + return response; +} +function clarify_devalue_error(event, error) { + if (error.path) { + return `Data returned from \`load\` while rendering ${event.route.id} is not serializable: ${error.message} (${error.path}). If you need to serialize/deserialize custom types, use transport hooks: https://svelte.dev/docs/kit/hooks#Universal-hooks-transport.`; + } + if (error.path === "") { + return `Data returned from \`load\` while rendering ${event.route.id} is not a plain object`; + } + return error.message; +} +function serialize_uses(node) { + const uses = {}; + if (node.uses && node.uses.dependencies.size > 0) { + uses.dependencies = Array.from(node.uses.dependencies); + } + if (node.uses && node.uses.search_params.size > 0) { + uses.search_params = Array.from(node.uses.search_params); + } + if (node.uses && node.uses.params.size > 0) { + uses.params = Array.from(node.uses.params); + } + if (node.uses?.parent) uses.parent = 1; + if (node.uses?.route) uses.route = 1; + if (node.uses?.url) uses.url = 1; + return uses; +} +function has_prerendered_path(manifest, pathname) { + return manifest._.prerendered_routes.has(pathname) || pathname.at(-1) === "/" && manifest._.prerendered_routes.has(pathname.slice(0, -1)); +} +function format_server_error(status, error, event) { + const formatted_text = ` +\x1B[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1B[0m`; + if (status === 404) { + return formatted_text; + } + return `${formatted_text} +${error.stack}`; +} +function get_node_type(node_id) { + const parts = node_id?.split("/"); + const filename = parts?.at(-1); + if (!filename) return "unknown"; + const dot_parts = filename.split("."); + return dot_parts.slice(0, -1).join("."); +} +const INVALIDATED_PARAM = "x-sveltekit-invalidated"; +const TRAILING_SLASH_PARAM = "x-sveltekit-trailing-slash"; +function stringify(data, transport) { + const encoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.encode])); + return devalue.stringify(data, encoders); +} +function stringify_remote_arg(value, transport) { + if (value === void 0) return ""; + const json_string = stringify(value, transport); + const bytes = new TextEncoder().encode(json_string); + return base64_encode(bytes).replaceAll("=", "").replaceAll("+", "-").replaceAll("/", "_"); +} +function parse_remote_arg(string, transport) { + if (!string) return void 0; + const json_string = text_decoder.decode( + // no need to add back `=` characters, atob can handle it + base64_decode(string.replaceAll("-", "+").replaceAll("_", "/")) + ); + const decoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.decode])); + return devalue.parse(json_string, decoders); +} +function create_remote_key(id, payload) { + return id + "/" + payload; +} +export { + ENDPOINT_METHODS as E, + INVALIDATED_PARAM as I, + MUTATIVE_METHODS as M, + PAGE_METHODS as P, + SVELTE_KIT_ASSETS as S, + TRAILING_SLASH_PARAM as T, + normalize_error as a, + get_global_name as b, + clarify_devalue_error as c, + get_node_type as d, + escape_html as e, + create_remote_key as f, + get_status as g, + handle_error_and_jsonify as h, + is_form_content_type as i, + static_error_page as j, + stringify as k, + deserialize_binary_form as l, + method_not_allowed as m, + negotiate as n, + has_prerendered_path as o, + parse_remote_arg as p, + handle_fatal_error as q, + redirect_response as r, + serialize_uses as s, + format_server_error as t, + create_field_proxy as u, + normalize_issue as v, + set_nested_value as w, + flatten_issues as x, + deep_set as y, + stringify_remote_arg as z +}; diff --git a/.svelte-kit/output/server/chunks/state.svelte.js b/.svelte-kit/output/server/chunks/state.svelte.js new file mode 100644 index 0000000..9cd4558 --- /dev/null +++ b/.svelte-kit/output/server/chunks/state.svelte.js @@ -0,0 +1,18 @@ +import "clsx"; +import { n as noop } from "./index3.js"; +import "./url.js"; +import "@sveltejs/kit/internal/server"; +import "./root.js"; +const is_legacy = noop.toString().includes("$$") || /function \w+\(\) \{\}/.test(noop.toString()); +if (is_legacy) { + ({ + data: {}, + form: null, + error: null, + params: {}, + route: { id: null }, + state: {}, + status: -1, + url: new URL("https://example.com") + }); +} diff --git a/.svelte-kit/output/server/chunks/url.js b/.svelte-kit/output/server/chunks/url.js new file mode 100644 index 0000000..2ba2c18 --- /dev/null +++ b/.svelte-kit/output/server/chunks/url.js @@ -0,0 +1,106 @@ +const SCHEME = /^[a-z][a-z\d+\-.]+:/i; +const internal = new URL("sveltekit-internal://"); +function resolve(base, path) { + if (path[0] === "/" && path[1] === "/") return path; + let url = new URL(base, internal); + url = new URL(path, url); + return url.protocol === internal.protocol ? url.pathname + url.search + url.hash : url.href; +} +function normalize_path(path, trailing_slash) { + if (path === "/" || trailing_slash === "ignore") return path; + if (trailing_slash === "never") { + return path.endsWith("/") ? path.slice(0, -1) : path; + } else if (trailing_slash === "always" && !path.endsWith("/")) { + return path + "/"; + } + return path; +} +function decode_pathname(pathname) { + return pathname.split("%25").map(decodeURI).join("%25"); +} +function decode_params(params) { + for (const key in params) { + params[key] = decodeURIComponent(params[key]); + } + return params; +} +function make_trackable(url, callback, search_params_callback, allow_hash = false) { + const tracked = new URL(url); + Object.defineProperty(tracked, "searchParams", { + value: new Proxy(tracked.searchParams, { + get(obj, key) { + if (key === "get" || key === "getAll" || key === "has") { + return (param, ...rest) => { + search_params_callback(param); + return obj[key](param, ...rest); + }; + } + callback(); + const value = Reflect.get(obj, key); + return typeof value === "function" ? value.bind(obj) : value; + } + }), + enumerable: true, + configurable: true + }); + const tracked_url_properties = ["href", "pathname", "search", "toString", "toJSON"]; + if (allow_hash) tracked_url_properties.push("hash"); + for (const property of tracked_url_properties) { + Object.defineProperty(tracked, property, { + get() { + callback(); + return url[property]; + }, + enumerable: true, + configurable: true + }); + } + { + tracked[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { + return inspect(url, opts); + }; + tracked.searchParams[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { + return inspect(url.searchParams, opts); + }; + } + if (!allow_hash) { + disable_hash(tracked); + } + return tracked; +} +function disable_hash(url) { + allow_nodejs_console_log(url); + Object.defineProperty(url, "hash", { + get() { + throw new Error( + "Cannot access event.url.hash. Consider using `page.url.hash` inside a component instead" + ); + } + }); +} +function disable_search(url) { + allow_nodejs_console_log(url); + for (const property of ["search", "searchParams"]) { + Object.defineProperty(url, property, { + get() { + throw new Error(`Cannot access url.${property} on a page with prerendering enabled`); + } + }); + } +} +function allow_nodejs_console_log(url) { + { + url[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { + return inspect(new URL(url), opts); + }; + } +} +export { + SCHEME as S, + decode_pathname as a, + decode_params as b, + disable_search as d, + make_trackable as m, + normalize_path as n, + resolve as r +}; diff --git a/.svelte-kit/output/server/chunks/utils.js b/.svelte-kit/output/server/chunks/utils.js new file mode 100644 index 0000000..78e5bde --- /dev/null +++ b/.svelte-kit/output/server/chunks/utils.js @@ -0,0 +1,43 @@ +const text_encoder = new TextEncoder(); +const text_decoder = new TextDecoder(); +function get_relative_path(from, to) { + const from_parts = from.split(/[/\\]/); + const to_parts = to.split(/[/\\]/); + from_parts.pop(); + while (from_parts[0] === to_parts[0]) { + from_parts.shift(); + to_parts.shift(); + } + let i = from_parts.length; + while (i--) from_parts[i] = ".."; + return from_parts.concat(to_parts).join("/"); +} +function base64_encode(bytes) { + if (globalThis.Buffer) { + return globalThis.Buffer.from(bytes).toString("base64"); + } + let binary = ""; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} +function base64_decode(encoded) { + if (globalThis.Buffer) { + const buffer = globalThis.Buffer.from(encoded, "base64"); + return new Uint8Array(buffer); + } + const binary = atob(encoded); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +} +export { + text_encoder as a, + base64_encode as b, + base64_decode as c, + get_relative_path as g, + text_decoder as t +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/health/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/health/_server.ts.js new file mode 100644 index 0000000..203529d --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/health/_server.ts.js @@ -0,0 +1,25 @@ +import { json } from "@sveltejs/kit"; +import { d as db } from "../../../../chunks/index2.js"; +import { sql } from "drizzle-orm"; +const GET = async () => { + try { + await db.execute(sql`SELECT 1`); + return json({ + status: "ok", + timestamp: (/* @__PURE__ */ new Date()).toISOString(), + uptime: process.uptime() + }); + } catch (e) { + return json( + { + status: "error", + error: "Database connection failed", + timestamp: (/* @__PURE__ */ new Date()).toISOString() + }, + { status: 503 } + ); + } +}; +export { + GET +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/_server.ts.js new file mode 100644 index 0000000..4d6b270 --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/_server.ts.js @@ -0,0 +1,24 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, m as mappool, a as mappoolSlot } from "../../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const GET = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const pool = await db.query.mappool.findFirst({ + where: eq(mappool.id, params.id) + }); + if (!pool) error(404, "Mappool not found"); + const slots = await db.query.mappoolSlot.findMany({ + where: eq(mappoolSlot.mappoolId, params.id), + orderBy: (s, { asc }) => [asc(s.category), asc(s.orderInCategory)] + }); + return json({ ...pool, slots }); +}; +const DELETE = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + await db.delete(mappool).where(eq(mappool.id, params.id)); + return json({ ok: true }); +}; +export { + DELETE, + GET +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/slots/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/slots/_server.ts.js new file mode 100644 index 0000000..b61da5c --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/slots/_server.ts.js @@ -0,0 +1,27 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, a as mappoolSlot } from "../../../../../../chunks/index2.js"; +import { and, eq } from "drizzle-orm"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { beatmapId, category, mods } = await request.json(); + if (!beatmapId || !category) { + error(400, "beatmapId and category are required"); + } + const existing = await db.query.mappoolSlot.findMany({ + where: and( + eq(mappoolSlot.mappoolId, params.id), + eq(mappoolSlot.category, category.toUpperCase()) + ) + }); + const [created] = await db.insert(mappoolSlot).values({ + mappoolId: params.id, + beatmapId: String(beatmapId), + category: category.toUpperCase(), + orderInCategory: existing.length + 1, + mods: mods ?? [] + }).returning(); + return json(created, { status: 201 }); +}; +export { + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js new file mode 100644 index 0000000..c77f59a --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js @@ -0,0 +1,11 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, a as mappoolSlot } from "../../../../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const DELETE = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + await db.delete(mappoolSlot).where(eq(mappoolSlot.id, params.slotId)); + return json({ ok: true }); +}; +export { + DELETE +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/mappools/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/mappools/_server.ts.js new file mode 100644 index 0000000..bf904be --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/mappools/_server.ts.js @@ -0,0 +1,23 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db, m as mappool } from "../../../../chunks/index2.js"; +const GET = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const mappools = await db.query.mappool.findMany({ + orderBy: (m, { desc }) => [desc(m.createdAt)] + }); + return json(mappools); +}; +const POST = async ({ request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { name } = await request.json(); + if (!name?.trim()) error(400, "Name is required"); + const [created] = await db.insert(mappool).values({ + name: name.trim(), + createdBy: locals.user.id + }).returning(); + return json(created, { status: 201 }); +}; +export { + GET, + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/_server.ts.js new file mode 100644 index 0000000..b383e98 --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/_server.ts.js @@ -0,0 +1,24 @@ +import { error, json } from "@sveltejs/kit"; +import { a as cancelMatch, g as getMatchFull } from "../../../../../chunks/engine.js"; +const GET = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const m = await getMatchFull(params.id); + return json(m); + } catch (e) { + error(404, e.message); + } +}; +const DELETE = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const m = await cancelMatch(params.id); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + DELETE, + GET +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/lobby/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/lobby/_server.ts.js new file mode 100644 index 0000000..35ed7aa --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/lobby/_server.ts.js @@ -0,0 +1,15 @@ +import { error, json } from "@sveltejs/kit"; +import { m as moveToLobby } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const body = await request.json().catch(() => ({})); + try { + const m = await moveToLobby(params.id, body.osuLobbyId); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/pick/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/pick/_server.ts.js new file mode 100644 index 0000000..a655c49 --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/pick/_server.ts.js @@ -0,0 +1,18 @@ +import { error, json } from "@sveltejs/kit"; +import { p as pickMap } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { participantId, mappoolSlotId } = await request.json(); + if (!participantId || !mappoolSlotId) { + error(400, "participantId and mappoolSlotId required"); + } + try { + const game = await pickMap(params.id, participantId, mappoolSlotId); + return json(game); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/roll/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/roll/_server.ts.js new file mode 100644 index 0000000..21f102e --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/roll/_server.ts.js @@ -0,0 +1,18 @@ +import { error, json } from "@sveltejs/kit"; +import { s as submitRoll } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { participantId, value } = await request.json(); + if (!participantId || value == null) { + error(400, "participantId and value required"); + } + try { + const m = await submitRoll(params.id, participantId, value); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/rolling/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/rolling/_server.ts.js new file mode 100644 index 0000000..e5cc1e3 --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/rolling/_server.ts.js @@ -0,0 +1,14 @@ +import { error, json } from "@sveltejs/kit"; +import { b as moveToRolling } from "../../../../../../chunks/engine.js"; +const POST = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const m = await moveToRolling(params.id); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/score/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/score/_server.ts.js new file mode 100644 index 0000000..cd031cb --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/matches/_id_/score/_server.ts.js @@ -0,0 +1,18 @@ +import { error, json } from "@sveltejs/kit"; +import { d as submitGameScores } from "../../../../../../chunks/engine.js"; +const POST = async ({ request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const { matchGameId, scores } = await request.json(); + if (!matchGameId || !scores?.length) { + error(400, "matchGameId and scores required"); + } + try { + const m = await submitGameScores(matchGameId, scores); + return json(m); + } catch (e) { + error(400, e.message); + } +}; +export { + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/matches/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/matches/_server.ts.js new file mode 100644 index 0000000..3732a25 --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/matches/_server.ts.js @@ -0,0 +1,37 @@ +import { error, json } from "@sveltejs/kit"; +import { d as db } from "../../../../chunks/index2.js"; +import { c as createMatch } from "../../../../chunks/engine.js"; +const GET = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const matches = await db.query.match.findMany({ + with: { + participants: { with: { team: true } } + }, + orderBy: (m, { desc }) => [desc(m.createdAt)] + }); + return json(matches); +}; +const POST = async ({ request, locals }) => { + if (!locals.user) error(401, "Not logged in"); + const body = await request.json(); + const { name, config, mappoolId, teams } = body; + if (!name || !config || !mappoolId || !teams?.length) { + error(400, "Missing required fields"); + } + try { + const result = await createMatch({ + name, + config, + mappoolId, + teams, + createdBy: locals.user.id + }); + return json(result, { status: 201 }); + } catch (e) { + error(400, e.message); + } +}; +export { + GET, + POST +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/osu/beatmap/_id_/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/osu/beatmap/_id_/_server.ts.js new file mode 100644 index 0000000..c258470 --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/osu/beatmap/_id_/_server.ts.js @@ -0,0 +1,14 @@ +import { error, json } from "@sveltejs/kit"; +import { g as getBeatmap } from "../../../../../../chunks/api.js"; +const GET = async ({ params, locals }) => { + if (!locals.user) error(401, "Not logged in"); + try { + const beatmap = await getBeatmap(params.id); + return json(beatmap); + } catch { + error(404, "Beatmap not found"); + } +}; +export { + GET +}; diff --git a/.svelte-kit/output/server/entries/endpoints/api/queue/_server.ts.js b/.svelte-kit/output/server/entries/endpoints/api/queue/_server.ts.js new file mode 100644 index 0000000..d6b0508 --- /dev/null +++ b/.svelte-kit/output/server/entries/endpoints/api/queue/_server.ts.js @@ -0,0 +1,36 @@ +import { error, json } from "@sveltejs/kit"; +import { l as leaveQueue, e as getQueueStatus, j as joinQueue } from "../../../../chunks/engine.js"; +import { d as db, t as teamMember } from "../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const GET = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const status = await getQueueStatus(locals.user.id); + return json(status); +}; +const POST = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + const membership = await db.query.teamMember.findFirst({ + where: eq(teamMember.userId, locals.user.id), + with: { team: true } + }); + const personalTeam = membership?.team; + if (!personalTeam?.isPersonal) { + error(400, "No personal team found"); + } + try { + const result = await joinQueue(locals.user.id, personalTeam.id); + return json({ matched: !!result, match: result }); + } catch (e) { + error(400, e.message); + } +}; +const DELETE = async ({ locals }) => { + if (!locals.user) error(401, "Not logged in"); + await leaveQueue(locals.user.id); + return json({ ok: true }); +}; +export { + DELETE, + GET, + POST +}; diff --git a/.svelte-kit/output/server/entries/fallbacks/layout.svelte.js b/.svelte-kit/output/server/entries/fallbacks/layout.svelte.js new file mode 100644 index 0000000..c3792df --- /dev/null +++ b/.svelte-kit/output/server/entries/fallbacks/layout.svelte.js @@ -0,0 +1,9 @@ +import "clsx"; +function Layout($$renderer, $$props) { + let { children } = $$props; + children($$renderer); + $$renderer.push(``); +} +export { + Layout as default +}; diff --git a/.svelte-kit/output/server/entries/hooks.server.js b/.svelte-kit/output/server/entries/hooks.server.js new file mode 100644 index 0000000..35278f0 --- /dev/null +++ b/.svelte-kit/output/server/entries/hooks.server.js @@ -0,0 +1,22 @@ +import { b as building } from "../chunks/environment.js"; +import { a as auth, s as svelteKitHandler } from "../chunks/auth.js"; +import { d as db, u as user } from "../chunks/index2.js"; +import { eq } from "drizzle-orm"; +const handleBetterAuth = async ({ event, resolve }) => { + const session = await auth.api.getSession({ headers: event.request.headers }); + if (session) { + event.locals.session = session.session; + const dbUser = await db.query.user.findFirst({ + where: eq(user.id, session.user.id) + }); + event.locals.user = { + ...session.user, + role: dbUser?.role ?? "player" + }; + } + return svelteKitHandler({ event, resolve, auth, building }); +}; +const handle = handleBetterAuth; +export { + handle +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/_layout.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/_layout.server.ts.js new file mode 100644 index 0000000..18d72d1 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/_layout.server.ts.js @@ -0,0 +1,14 @@ +const load = async ({ locals }) => { + return { + user: locals.user ? { + id: locals.user.id, + name: locals.user.name, + email: locals.user.email, + image: locals.user.image, + role: locals.user.role ?? "player" + } : null + }; +}; +export { + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/_layout.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/_layout.svelte.js new file mode 100644 index 0000000..068b523 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/_layout.svelte.js @@ -0,0 +1,97 @@ +import { a as ensure_array_like, b as attr, c as attr_class, s as stringify, e as escape_html, d as derived } from "../../../chunks/index3.js"; +import { p as page } from "../../../chunks/index4.js"; +import "@sveltejs/kit/internal"; +import "../../../chunks/url.js"; +import "../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../chunks/root.js"; +import "../../../chunks/exports.js"; +import "../../../chunks/state.svelte.js"; +function Sidebar($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + const user = derived(() => page.data?.user); + const nav = [ + { href: "/", label: "Dashboard", icon: "⌂" }, + { href: "/matches", label: "Matches", icon: "⚔" }, + { href: "/mappools", label: "Mappools", icon: "♫" }, + { href: "/teams", label: "Teams", icon: "⚑" } + ]; + function isActive(href) { + if (href === "/") return page.url.pathname === "/"; + return page.url.pathname.startsWith(href); + } + const isAdmin = derived(() => user()?.role === "admin"); + $$renderer2.push(``); + }); +} +function Header($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + const user = derived(() => page.data?.user); + const roleBadge = { + referee: { label: "REF", color: "bg-yellow-500/20 text-yellow-400" }, + admin: { label: "ADMIN", color: "bg-red-500/20 text-red-400" } + }; + const badge = derived(() => user()?.role ? roleBadge[user().role] : null); + $$renderer2.push(`
`); + if (user()) { + $$renderer2.push(""); + $$renderer2.push(`
`); + if (badge()) { + $$renderer2.push(""); + $$renderer2.push(`${escape_html(badge().label)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (user().image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(user().name)}
`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Sign in with osu!`); + } + $$renderer2.push(`
`); + }); +} +function _layout($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { children } = $$props; + const user = derived(() => page.data?.user); + if (user()) { + $$renderer2.push(""); + $$renderer2.push(`
`); + Sidebar($$renderer2); + $$renderer2.push(`
`); + Header($$renderer2); + $$renderer2.push(`
`); + children($$renderer2); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + $$renderer2.push(`
V
Vash Esports
Sign in with osu!
`); + children($$renderer2); + $$renderer2.push(`
`); + } + $$renderer2.push(``); + }); +} +export { + _layout as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/_page.server.ts.js new file mode 100644 index 0000000..e04978c --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/_page.server.ts.js @@ -0,0 +1,39 @@ +import { d as db, b as match, c as team, m as mappool } from "../../../chunks/index2.js"; +import { desc, inArray, count, eq } from "drizzle-orm"; +const load = async ({ locals }) => { + if (!locals.user) { + return { + authenticated: false, + recentMatches: [], + liveMatches: [], + stats: { matches: 0, finished: 0, teams: 0, mappools: 0 } + }; + } + const recentMatches = await db.query.match.findMany({ + with: { participants: { with: { team: true } } }, + orderBy: desc(match.createdAt), + limit: 5 + }); + const liveMatches = await db.query.match.findMany({ + with: { participants: { with: { team: true } } }, + where: inArray(match.state, ["LOBBY", "ROLLING", "PICKING", "PLAYING"]) + }); + const [matchCount] = await db.select({ count: count() }).from(match); + const [teamCount] = await db.select({ count: count() }).from(team); + const [poolCount] = await db.select({ count: count() }).from(mappool); + const [finishedCount] = await db.select({ count: count() }).from(match).where(eq(match.state, "FINISHED")); + return { + authenticated: true, + recentMatches, + liveMatches, + stats: { + matches: matchCount.count, + finished: finishedCount.count, + teams: teamCount.count, + mappools: poolCount.count + } + }; +}; +export { + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/_page.svelte.js new file mode 100644 index 0000000..6f98657 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/_page.svelte.js @@ -0,0 +1,118 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify, c as attr_class } from "../../../chunks/index3.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const stateConfig = { + CREATED: { label: "Created", color: "text-text-secondary" }, + LOBBY: { label: "In Lobby", color: "text-yellow-400" }, + ROLLING: { + label: "Rolling", + color: "text-yellow-400", + dot: "bg-yellow-400" + }, + PICKING: { label: "Picking", color: "text-blue-400", dot: "bg-blue-400" }, + PLAYING: { label: "Live", color: "text-green-400", dot: "bg-green-400" }, + FINISHED: { label: "Finished", color: "text-text-secondary" }, + CANCELLED: { label: "Cancelled", color: "text-red-400" } + }; + let queueLoading = false; + function timeAgo(date) { + const d = new Date(date); + const diff = Date.now() - d.getTime(); + const mins = Math.floor(diff / 6e4); + if (mins < 1) return "just now"; + if (mins < 60) return `${mins}m ago`; + const hours = Math.floor(mins / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; + } + if (!data.user) { + $$renderer2.push(""); + $$renderer2.push(`
V

Welcome to Vash Esports

Automated tournament match management for osu! — lobbies, mappools, picks, bans, and + ELO tracking all in one place.

Sign in with osu!

Uses your osu! account. No extra registration needed.

`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

Welcome back, ${escape_html(data.user.name)}

Automated esports match management for osu!

${escape_html(data.stats.matches)}

Total Matches

${escape_html(data.stats.finished)}

Completed

${escape_html(data.stats.teams)}

Teams

${escape_html(data.stats.mappools)}

Mappools

`); + if (data.liveMatches.length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Live Now

`); + const each_array = ensure_array_like(data.liveMatches); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let m = each_array[$$index]; + const p1 = m.participants[0]; + const p2 = m.participants[1]; + const sc = stateConfig[m.state]; + $$renderer2.push(`
`); + if (p1?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(p1?.team.name ?? "?")}
${escape_html(p1?.score ?? 0)} vs ${escape_html(p2?.score ?? 0)}
${escape_html(p2?.team.name ?? "?")} `); + if (p2?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (sc?.dot) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label)}`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

Ranked Queue

Find a match at your skill level

`); + { + $$renderer2.push(""); + $$renderer2.push(`${escape_html("Find Match")}`); + } + $$renderer2.push(`

Recent Matches

`); + const each_array_1 = ensure_array_like(data.recentMatches); + if (each_array_1.length !== 0) { + $$renderer2.push(""); + for (let $$index_1 = 0, $$length = each_array_1.length; $$index_1 < $$length; $$index_1++) { + let m = each_array_1[$$index_1]; + const p1 = m.participants[0]; + const p2 = m.participants[1]; + const config = m.config; + const sc = stateConfig[m.state]; + $$renderer2.push(`
${escape_html(p1?.team.name ?? "?")} `); + if (m.state === "FINISHED") { + $$renderer2.push(""); + $$renderer2.push(` (p2?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p1?.score ?? 0)} - (p1?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p2?.score ?? 0)}`); + } else { + $$renderer2.push(""); + $$renderer2.push(`vs`); + } + $$renderer2.push(` ${escape_html(p2?.team.name ?? "?")}

${escape_html(m.name ? `${m.name} · ` : "")}BO${escape_html(config.bestOf)} + · ${escape_html(timeAgo(m.finishedAt ?? m.createdAt))}

`); + if (m.state === "FINISHED" && m.winnerId) { + $$renderer2.push(""); + const winner = m.participants.find((p) => p.teamId === m.winnerId); + $$renderer2.push(`🏆 ${escape_html(winner?.team.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label)}`); + } + } else { + $$renderer2.push(""); + $$renderer2.push(`

No matches yet. Create one

`); + } + $$renderer2.push(`
`); + } + $$renderer2.push(``); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/admin/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/admin/_page.server.ts.js new file mode 100644 index 0000000..7f5e1cf --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/admin/_page.server.ts.js @@ -0,0 +1,70 @@ +import { d as db, u as user, p as playerRating } from "../../../../chunks/index2.js"; +import { eq, desc } from "drizzle-orm"; +import { error } from "@sveltejs/kit"; +const ROLE_HIERARCHY = { + player: 0, + referee: 1, + admin: 2 +}; +function hasRole(userRole, minRole) { + const level = ROLE_HIERARCHY[userRole] ?? 0; + const required = ROLE_HIERARCHY[minRole]; + return level >= required; +} +function requireRole(locals, minRole, message = "Insufficient permissions") { + if (!locals.user) { + error(401, "Not logged in"); + } + if (!hasRole(locals.user.role, minRole)) { + error(403, message); + } +} +async function setUserRole(userId, role) { + await db.update(user).set({ role }).where(eq(user.id, userId)); +} +const load = async ({ locals }) => { + requireRole(locals, "admin"); + const users = await db.query.user.findMany({ + orderBy: desc(user.createdAt) + }); + const usersWithRatings = await Promise.all( + users.map(async (u) => { + const rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, u.id) + }); + return { + id: u.id, + name: u.name, + email: u.email, + image: u.image, + role: u.role ?? "player", + createdAt: u.createdAt, + elo: rating?.elo ?? 1e3, + wins: rating?.wins ?? 0, + losses: rating?.losses ?? 0 + }; + }) + ); + return { users: usersWithRatings }; +}; +const actions = { + setRole: async ({ request, locals }) => { + requireRole(locals, "admin"); + const form = await request.formData(); + const userId = form.get("userId")?.toString(); + const role = form.get("role")?.toString(); + if (!userId || !role) return { error: "User ID and role are required" }; + if (!["player", "referee", "admin"].includes(role)) { + return { error: "Invalid role" }; + } + if (userId === locals.user.id && role !== "admin") { + return { error: "You can't demote yourself" }; + } + await setUserRole(userId, role); + return { success: true }; + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/admin/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/admin/_page.svelte.js new file mode 100644 index 0000000..59b9970 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/admin/_page.svelte.js @@ -0,0 +1,70 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const roleBadge = { + player: { + label: "Player", + color: "bg-surface-600 text-text-secondary border-border" + }, + referee: { + label: "Referee", + color: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30" + }, + admin: { + label: "Admin", + color: "bg-red-500/20 text-red-400 border-red-500/30" + } + }; + $$renderer2.push(`

Admin Panel

${escape_html(data.users.length)} registered user${escape_html(data.users.length !== 1 ? "s" : "")}

`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
Avatar User ELO W/L Role
`); + const each_array = ensure_array_like(data.users); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let u = each_array[$$index]; + const badge = roleBadge[u.role] ?? roleBadge.player; + $$renderer2.push(`
`); + if (u.image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
${escape_html(u.name.charAt(0).toUpperCase())}
`); + } + $$renderer2.push(`

${escape_html(u.name)}

${escape_html(u.email)}

${escape_html(u.elo)} ${escape_html(u.wins)}W / ${escape_html(u.losses)}L
`); + $$renderer2.select( + { + name: "role", + value: u.role, + onchange: (e) => e.currentTarget.form?.requestSubmit(), + class: `rounded-md border px-2 py-1 text-xs font-500 ${stringify(badge.color)} cursor-pointer bg-transparent focus:outline-none` + }, + ($$renderer3) => { + $$renderer3.option({ value: "player", class: "bg-surface-800 text-text-primary" }, ($$renderer4) => { + $$renderer4.push(`Player`); + }); + $$renderer3.option({ value: "referee", class: "bg-surface-800 text-text-primary" }, ($$renderer4) => { + $$renderer4.push(`Referee`); + }); + $$renderer3.option({ value: "admin", class: "bg-surface-800 text-text-primary" }, ($$renderer4) => { + $$renderer4.push(`Admin`); + }); + } + ); + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/mappools/_id_/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/mappools/_id_/_page.server.ts.js new file mode 100644 index 0000000..cbdf47b --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/mappools/_id_/_page.server.ts.js @@ -0,0 +1,100 @@ +import { d as db, m as mappool, a as mappoolSlot } from "../../../../../chunks/index2.js"; +import { eq, and } from "drizzle-orm"; +import { redirect, error } from "@sveltejs/kit"; +import { g as getBeatmap } from "../../../../../chunks/api.js"; +const load = async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + const pool = await db.query.mappool.findFirst({ + where: eq(mappool.id, params.id), + with: { + slots: { + orderBy: (s, { asc }) => [asc(s.category), asc(s.orderInCategory)] + } + } + }); + if (!pool) error(404, "Mappool not found"); + const slotsWithBeatmaps = await Promise.all( + pool.slots.map(async (slot) => { + try { + const beatmap = await getBeatmap(slot.beatmapId); + return { + ...slot, + beatmap: { + title: beatmap.beatmapset.title, + artist: beatmap.beatmapset.artist, + version: beatmap.version, + starRating: beatmap.difficulty_rating, + bpm: beatmap.bpm, + totalLength: beatmap.total_length, + coverUrl: beatmap.beatmapset.covers["card@2x"], + url: beatmap.url + } + }; + } catch { + return { + ...slot, + beatmap: null + }; + } + }) + ); + return { + pool: { ...pool, slots: slotsWithBeatmaps } + }; +}; +const actions = { + addSlot: async ({ params, request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + let beatmapId = form.get("beatmapId")?.toString()?.trim(); + const category = form.get("category")?.toString()?.trim()?.toUpperCase(); + if (!beatmapId || !category) { + return { error: "Beatmap ID and category are required" }; + } + const urlMatch = beatmapId.match(/beatmaps\/(\d+)/); + const setMatch = beatmapId.match(/beatmapsets\/\d+#\w+\/(\d+)/); + if (urlMatch) beatmapId = urlMatch[1]; + else if (setMatch) beatmapId = setMatch[1]; + if (!/^\d+$/.test(beatmapId)) { + return { error: "Invalid beatmap ID" }; + } + let beatmap; + try { + beatmap = await getBeatmap(beatmapId); + } catch { + return { error: "Beatmap not found on osu!" }; + } + const existing = await db.query.mappoolSlot.findMany({ + where: and( + eq(mappoolSlot.mappoolId, params.id), + eq(mappoolSlot.category, category) + ) + }); + await db.insert(mappoolSlot).values({ + mappoolId: params.id, + beatmapId, + category, + orderInCategory: existing.length + 1, + starRating: beatmap.difficulty_rating, + mods: category === "NM" || category === "TB" || category === "FM" ? [] : [category] + }); + return { success: true }; + }, + removeSlot: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const slotId = form.get("slotId")?.toString(); + if (!slotId) return { error: "Missing slot ID" }; + await db.delete(mappoolSlot).where(eq(mappoolSlot.id, slotId)); + return { success: true }; + }, + deletePool: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + await db.delete(mappool).where(eq(mappool.id, params.id)); + redirect(303, "/mappools"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/mappools/_id_/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/mappools/_id_/_page.svelte.js new file mode 100644 index 0000000..0a8825e --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/mappools/_id_/_page.svelte.js @@ -0,0 +1,98 @@ +import { e as escape_html, a as ensure_array_like, b as attr, c as attr_class, d as derived, s as stringify } from "../../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../../chunks/url.js"; +import "../../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../../chunks/root.js"; +import "../../../../../chunks/exports.js"; +import "../../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const categories = ["NM", "HD", "HR", "DT", "FM", "TB"]; + let addingCategory = "NM"; + let beatmapInput = ""; + const grouped = derived(() => () => { + const groups = {}; + for (const cat of categories) { + const slots = data.pool.slots.filter((s) => s.category === cat); + if (slots.length > 0) groups[cat] = slots; + } + return groups; + }); + function formatLength(seconds) { + const m = Math.floor(seconds / 60); + const s = seconds % 60; + return `${m}:${s.toString().padStart(2, "0")}`; + } + const categoryColors = { + NM: "bg-blue-500/20 text-blue-400 border-blue-500/30", + HD: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", + HR: "bg-red-500/20 text-red-400 border-red-500/30", + DT: "bg-purple-500/20 text-purple-400 border-purple-500/30", + FM: "bg-green-500/20 text-green-400 border-green-500/30", + TB: "bg-pink-500/20 text-pink-400 border-pink-500/30" + }; + $$renderer2.push(`
←

${escape_html(data.pool.name)}

${escape_html(data.pool.slots.length)} map${escape_html(data.pool.slots.length !== 1 ? "s" : "")}

Add Map

`); + $$renderer2.select( + { + name: "category", + value: addingCategory, + class: "rounded-md border border-border bg-surface-700 px-3 py-2 text-sm text-text-primary focus:border-accent focus:outline-none" + }, + ($$renderer3) => { + $$renderer3.push(``); + const each_array = ensure_array_like(categories); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let cat = each_array[$$index]; + $$renderer3.option({ value: cat }, ($$renderer4) => { + $$renderer4.push(`${escape_html(cat)}`); + }); + } + $$renderer3.push(``); + } + ); + $$renderer2.push(`
`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array_1 = ensure_array_like(Object.entries(grouped()())); + if (each_array_1.length !== 0) { + $$renderer2.push(""); + for (let $$index_2 = 0, $$length = each_array_1.length; $$index_2 < $$length; $$index_2++) { + let [category, slots] = each_array_1[$$index_2]; + $$renderer2.push(`
${escape_html(category)} ${escape_html(slots.length)} map${escape_html(slots.length !== 1 ? "s" : "")}
`); + const each_array_2 = ensure_array_like(slots); + for (let i = 0, $$length2 = each_array_2.length; i < $$length2; i++) { + let slot = each_array_2[i]; + $$renderer2.push(`
`); + if (slot.beatmap?.coverUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
No cover
`); + } + $$renderer2.push(`
`); + if (slot.beatmap) { + $$renderer2.push(""); + $$renderer2.push(`${escape_html(slot.beatmap.artist)} - ${escape_html(slot.beatmap.title)}
[${escape_html(slot.beatmap.version)}] ★ ${escape_html(slot.beatmap.starRating.toFixed(2))} ${escape_html(slot.beatmap.bpm)} BPM ${escape_html(formatLength(slot.beatmap.totalLength))}
`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

Beatmap #${escape_html(slot.beatmapId)}

`); + } + $$renderer2.push(`
${escape_html(category)}${escape_html(i + 1)}
`); + } + $$renderer2.push(`
`); + } + } else { + $$renderer2.push(""); + $$renderer2.push(`

No maps yet. Add your first map above.

`); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/mappools/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/mappools/_page.server.ts.js new file mode 100644 index 0000000..77c98ad --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/mappools/_page.server.ts.js @@ -0,0 +1,24 @@ +import { d as db, m as mappool } from "../../../../chunks/index2.js"; +import { redirect } from "@sveltejs/kit"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const mappools = await db.query.mappool.findMany({ + with: { slots: true }, + orderBy: (m, { desc }) => [desc(m.createdAt)] + }); + return { mappools }; +}; +const actions = { + create: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const name = form.get("name")?.toString()?.trim(); + if (!name) return { error: "Name is required" }; + const [created] = await db.insert(mappool).values({ name, createdBy: locals.user.id }).returning(); + redirect(303, `/mappools/${created.id}`); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/mappools/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/mappools/_page.svelte.js new file mode 100644 index 0000000..c122aec --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/mappools/_page.svelte.js @@ -0,0 +1,34 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + $$renderer2.push(`

Mappools

Create and manage your beatmap pools

`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array = ensure_array_like(data.mappools); + if (each_array.length !== 0) { + $$renderer2.push(""); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let pool = each_array[$$index]; + $$renderer2.push(`

${escape_html(pool.name)}

${escape_html(pool.slots.length)} map${escape_html(pool.slots.length !== 1 ? "s" : "")} + · Created ${escape_html(new Date(pool.createdAt).toLocaleDateString())}

→`); + } + } else { + $$renderer2.push(""); + $$renderer2.push(`

No mappools yet. Create one to get started.

`); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/matches/_id_/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/matches/_id_/_page.server.ts.js new file mode 100644 index 0000000..497a952 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/matches/_id_/_page.server.ts.js @@ -0,0 +1,129 @@ +import { d as db, u as user } from "../../../../../chunks/index2.js"; +import { a as cancelMatch, g as getMatchFull, p as pickMap, s as submitRoll } from "../../../../../chunks/engine.js"; +import { c as closeLobby, f as forceStartGame, p as playPickedMap, g as getLobby } from "../../../../../chunks/orchestrator.js"; +import { redirect, error } from "@sveltejs/kit"; +import { g as getBeatmap } from "../../../../../chunks/api.js"; +import { eq } from "drizzle-orm"; +const load = async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + let m; + try { + m = await getMatchFull(params.id); + } catch { + error(404, "Match not found"); + } + const beatmapCache = {}; + if (m.mappool?.slots) { + await Promise.allSettled( + m.mappool.slots.map(async (slot) => { + try { + const bm = await getBeatmap(slot.beatmapId); + beatmapCache[slot.beatmapId] = { + title: bm.beatmapset.title, + artist: bm.beatmapset.artist, + version: bm.version, + starRating: bm.difficulty_rating, + bpm: bm.bpm, + totalLength: bm.total_length, + coverUrl: bm.beatmapset.covers["card@2x"], + listCoverUrl: bm.beatmapset.covers["list@2x"] + }; + } catch { + } + }) + ); + } + return { match: m, beatmapCache, userId: locals.user.id }; +}; +const actions = { + reinvite: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + const lobby = getLobby(params.id); + if (!lobby) return { error: "No active IRC lobby for this match" }; + const m = await getMatchFull(params.id); + const invited = []; + for (const p of m.participants) { + for (const pl of p.players) { + const u = await db.query.user.findFirst({ where: eq(user.id, pl.userId) }); + if (u?.name) { + await lobby.invite(u.name); + invited.push(u.name); + } + } + } + return { reinvited: invited }; + }, + roll: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + const m = await getMatchFull(params.id); + let myParticipant = m.participants.find( + (p) => p.players.some((pl) => pl.userId === locals.user.id) + ); + if (!myParticipant) { + return { error: "You are not in this match" }; + } + if (myParticipant.rollValue !== null) { + const unrolled = m.participants.find((p) => p.rollValue === null); + if (unrolled) { + myParticipant = unrolled; + } else { + return { error: "All participants have already rolled" }; + } + } + const value = Math.floor(Math.random() * 100) + 1; + await submitRoll(params.id, myParticipant.id, value); + const lobby = getLobby(params.id); + if (lobby) { + lobby.chat(`${myParticipant.team?.name ?? "Player"} rolled ${value} (via web)`).catch(() => { + }); + const updated = await getMatchFull(params.id); + if (updated.state === "PICKING") { + const sorted = [...updated.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + lobby.chat( + `Rolls complete! ${sorted[0]?.team.name} picks first. Use !pick (e.g. !pick NM1) or pick in web UI.` + ).catch(() => { + }); + } + } + return { rolled: value, participantId: myParticipant.id }; + }, + pick: async ({ params, request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const slotId = form.get("slotId")?.toString(); + if (!slotId) return { error: "No map selected" }; + const m = await getMatchFull(params.id); + const sorted = [...m.participants].sort( + (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) + ); + const expectedIdx = m.games.length % sorted.length; + const expectedPicker = sorted[expectedIdx]; + if (!expectedPicker) return { error: "Cannot determine picker" }; + try { + const game = await pickMap(params.id, expectedPicker.id, slotId); + playPickedMap(params.id, game.id).catch( + (err) => console.error("[Match] IRC play failed:", err.message) + ); + return { picked: true, gameId: game.id }; + } catch (e) { + return { error: e.message }; + } + }, + forceStart: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + await forceStartGame(params.id); + return { started: true }; + }, + cancel: async ({ params, locals }) => { + if (!locals.user) redirect(302, "/"); + await cancelMatch(params.id); + await closeLobby(params.id); + redirect(303, "/matches"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/matches/_id_/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/matches/_id_/_page.svelte.js new file mode 100644 index 0000000..e6b854f --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/matches/_id_/_page.svelte.js @@ -0,0 +1,323 @@ +import { e as escape_html, b as attr, s as stringify, c as attr_class, a as ensure_array_like, d as derived } from "../../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../../chunks/url.js"; +import "../../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../../chunks/root.js"; +import "../../../../../chunks/exports.js"; +import "../../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + let m = derived(() => data.match); + let p1 = derived(() => m().participants[0]); + let p2 = derived(() => m().participants[1]); + const config = derived(() => m().config); + const winsNeeded = derived(() => Math.ceil(config().bestOf / 2)); + const playedSlotIds = derived(() => new Set(m().games.map((g) => g.mappoolSlotId))); + const sortedByPick = derived(() => [...m().participants].sort((a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99))); + const expectedPickerIdx = derived(() => m().games.length % sortedByPick().length); + const expectedPicker = derived(() => sortedByPick()[expectedPickerIdx()]); + const canRoll = derived(() => m().state === "ROLLING" && m().participants.some((p) => p.rollValue === null)); + const catColors = { + NM: "bg-blue-500/20 text-blue-400 border-blue-500/30", + HD: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", + HR: "bg-red-500/20 text-red-400 border-red-500/30", + DT: "bg-purple-500/20 text-purple-400 border-purple-500/30", + FM: "bg-green-500/20 text-green-400 border-green-500/30", + TB: "bg-pink-500/20 text-pink-400 border-pink-500/30" + }; + const groupedSlots = derived(() => () => { + if (!m().mappool?.slots) return {}; + const groups = {}; + for (const slot of m().mappool.slots) { + if (!groups[slot.category]) groups[slot.category] = []; + groups[slot.category].push(slot); + } + for (const cat of Object.keys(groups)) { + groups[cat].sort((a, b) => a.orderInCategory - b.orderInCategory); + } + return groups; + }); + let rolling = false; + let picking = false; + let forceStarting = false; + function formatLength(seconds) { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, "0")}`; + } + function stateLabel(s) { + return { + CREATED: "Waiting", + LOBBY: "In Lobby", + ROLLING: "Rolling", + PICKING: "Pick Phase", + PLAYING: "Playing", + FINISHED: "Finished", + CANCELLED: "Cancelled" + }[s] ?? s; + } + function stateBorder(s) { + return { + ROLLING: "border-yellow-500/30", + PICKING: "border-blue-500/30", + PLAYING: "border-green-500/30", + FINISHED: "border-accent/30" + }[s] ?? "border-border"; + } + $$renderer2.push(`
←

${escape_html(m().name || "Match")}

Best of ${escape_html(config().bestOf)} · First to ${escape_html(winsNeeded())} `); + if (m().osuLobbyId) { + $$renderer2.push(""); + $$renderer2.push(`· osu! mp/${escape_html(m().osuLobbyId)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

${escape_html(stateLabel(m().state))} `); + if (!["FINISHED", "CANCELLED"].includes(m().state)) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (p1() && p2()) { + $$renderer2.push(""); + $$renderer2.push(`
`); + if (p1().team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

${escape_html(p1().team.name)}

`); + if (p1().rollValue != null) { + $$renderer2.push(""); + $$renderer2.push(`

Roll: ${escape_html(p1().rollValue)}${escape_html(p1().pickOrder === 1 ? " ★" : "")}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
= winsNeeded() ? "text-green-400" : "text-text-primary")}`)}>${escape_html(p1().score)}
vs = winsNeeded() ? "text-green-400" : "text-text-primary")}`)}>${escape_html(p2().score)}

${escape_html(p2().team.name)}

`); + if (p2().rollValue != null) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(p2().pickOrder === 1 ? "★ " : "")}Roll: ${escape_html(p2().rollValue)}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (p2().team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (["LOBBY", "ROLLING", "PICKING", "PLAYING"].includes(m().state)) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "ROLLING") { + $$renderer2.push(""); + $$renderer2.push(`

🎲 Roll Phase

Highest roll picks first

`); + const each_array = ensure_array_like(m().participants); + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let p = each_array[$$index]; + $$renderer2.push(`

${escape_html(p.team.name)}

`); + if (p.rollValue != null) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(p.rollValue)}

`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

—

`); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + if (canRoll()) { + $$renderer2.push(""); + $$renderer2.push(`

or type !roll in osu! chat

`); + } else { + $$renderer2.push(""); + $$renderer2.push(`

Waiting for all rolls...

`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "PICKING") { + $$renderer2.push(""); + $$renderer2.push(`

🎯 Pick a Map

${escape_html(expectedPicker()?.team.name)}'s turn to pick

or type !pick NM1 in osu! chat

`); + { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array_1 = ensure_array_like(Object.entries(groupedSlots()())); + for (let $$index_2 = 0, $$length = each_array_1.length; $$index_2 < $$length; $$index_2++) { + let [category, slots] = each_array_1[$$index_2]; + $$renderer2.push(`
${escape_html(category)}
`); + const each_array_2 = ensure_array_like(slots); + for (let i = 0, $$length2 = each_array_2.length; i < $$length2; i++) { + let slot = each_array_2[i]; + const bm = data.beatmapCache[slot.beatmapId]; + const isPlayed = playedSlotIds().has(slot.id); + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "PLAYING") { + $$renderer2.push(""); + const currentGame = m().games[m().games.length - 1]; + const bm = currentGame ? data.beatmapCache[currentGame.slot?.beatmapId] : null; + $$renderer2.push(`
Now Playing
`); + if (bm) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(bm.artist)} - ${escape_html(bm.title)} [${escape_html(bm.version)}]

★${escape_html(bm.starRating.toFixed(1))} · ${escape_html(bm.bpm)}bpm · ${escape_html(formatLength(bm.totalLength))}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

Waiting for players to ready up & play in osu!...

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "FINISHED") { + $$renderer2.push(""); + const winner = m().participants.find((p) => p.teamId === m().winnerId); + $$renderer2.push(`
🏆

${escape_html(winner?.team.name)} wins!

${escape_html(p1()?.score)} – ${escape_html(p2()?.score)}

`); + const each_array_3 = ensure_array_like(m().games); + for (let $$index_3 = 0, $$length = each_array_3.length; $$index_3 < $$length; $$index_3++) { + let game = each_array_3[$$index_3]; + const bm = data.beatmapCache[game.slot?.beatmapId]; + const winnerP = m().participants.find((p) => p.id === game.winnerParticipantId); + $$renderer2.push(`
${escape_html(game.slot?.category)}${escape_html(game.slot?.orderInCategory)} ${escape_html(bm ? `${bm.artist} - ${bm.title}` : `#${game.slot?.beatmapId}`)} ${escape_html(winnerP?.team.name)}
`); + } + $$renderer2.push(`
Back to Dashboard
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().games.length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Games Played

`); + const each_array_4 = ensure_array_like(m().games); + for (let $$index_4 = 0, $$length = each_array_4.length; $$index_4 < $$length; $$index_4++) { + let game = each_array_4[$$index_4]; + const bm = data.beatmapCache[game.slot?.beatmapId]; + const pickerTeam = m().participants.find((p) => p.id === game.pickedByParticipantId)?.team; + const winnerTeam = m().participants.find((p) => p.id === game.winnerParticipantId)?.team; + $$renderer2.push(`
${escape_html(game.gameNumber)} `); + if (bm?.listCoverUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (bm) { + $$renderer2.push(""); + $$renderer2.push(`

${escape_html(bm.artist)} - ${escape_html(bm.title)} [${escape_html(bm.version)}]

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
${escape_html(game.slot?.category)}${escape_html(game.slot?.orderInCategory)} `); + if (pickerTeam) { + $$renderer2.push(""); + $$renderer2.push(`Picked by ${escape_html(pickerTeam.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (game.state === "FINISHED" && p1() && p2()) { + $$renderer2.push(""); + const p1Score = game.scores.filter((s) => s.player?.participantId === p1().id).reduce((sum, s) => sum + s.score, 0); + const p2Score = game.scores.filter((s) => s.player?.participantId === p2().id).reduce((sum, s) => sum + s.score, 0); + $$renderer2.push(`
p2Score ? "font-700 text-green-400" : "text-text-secondary")}>${escape_html(p1Score.toLocaleString())} - p1Score ? "font-700 text-green-400" : "text-text-secondary")}>${escape_html(p2Score.toLocaleString())}
`); + } else if (game.state === "PLAYING") { + $$renderer2.push(""); + $$renderer2.push(`Live`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (winnerTeam) { + $$renderer2.push(""); + $$renderer2.push(`✓ ${escape_html(winnerTeam.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "CREATED") { + $$renderer2.push(""); + $$renderer2.push(`

Creating osu! lobby...

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "LOBBY") { + $$renderer2.push(""); + $$renderer2.push(`

Lobby created. Waiting for players to join...

`); + if (m().osuLobbyId) { + $$renderer2.push(""); + $$renderer2.push(`

osu! mp/${escape_html(m().osuLobbyId)}

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (m().state === "CANCELLED") { + $$renderer2.push(""); + $$renderer2.push(`

This match was cancelled.

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(``); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/matches/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/matches/_page.server.ts.js new file mode 100644 index 0000000..2891ac2 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/matches/_page.server.ts.js @@ -0,0 +1,58 @@ +import { d as db, b as match } from "../../../../chunks/index2.js"; +import { redirect } from "@sveltejs/kit"; +import { desc } from "drizzle-orm"; +import { c as createMatch } from "../../../../chunks/engine.js"; +import { i as initMatchLobby } from "../../../../chunks/orchestrator.js"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const matches = await db.query.match.findMany({ + with: { participants: { with: { team: true } } }, + orderBy: desc(match.createdAt), + limit: 50 + }); + const teams = await db.query.team.findMany({ + orderBy: (t, { asc }) => [asc(t.name)] + }); + const mappools = await db.query.mappool.findMany({ + with: { slots: true }, + orderBy: (m, { desc: desc2 }) => [desc2(m.createdAt)] + }); + return { matches, teams, mappools }; +}; +const actions = { + createMatch: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const name = form.get("name")?.toString()?.trim() || "Custom Match"; + const team1Id = form.get("team1")?.toString(); + const team2Id = form.get("team2")?.toString(); + const mappoolId = form.get("mappool")?.toString(); + const bestOf = parseInt(form.get("bestOf")?.toString() ?? "7"); + console.log("[Matches] CREATE:", { name, team1Id, team2Id, mappoolId, bestOf }); + if (!team1Id || !team2Id || !mappoolId) { + return { error: "All fields are required" }; + } + let result; + try { + result = await createMatch({ + name, + config: { bestOf, teamSize: 1, scoringType: "score" }, + mappoolId, + teams: [team1Id, team2Id], + createdBy: locals.user.id + }); + console.log("[Matches] Created:", result.id); + } catch (e) { + console.error("[Matches] Error:", e); + return { error: e.message }; + } + initMatchLobby(result.id).catch((err) => { + console.error("[Matches] IRC lobby failed:", err.message); + }); + redirect(303, `/matches/${result.id}`); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/matches/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/matches/_page.svelte.js new file mode 100644 index 0000000..9af67ae --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/matches/_page.svelte.js @@ -0,0 +1,164 @@ +import { e as escape_html, a as ensure_array_like, b as attr, s as stringify, c as attr_class, d as derived } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const stateConfig = { + CREATED: { label: "Created", color: "text-text-secondary border-border" }, + LOBBY: { + label: "In Lobby", + color: "text-yellow-400 border-yellow-500/30" + }, + ROLLING: { + label: "Rolling", + color: "text-yellow-400 border-yellow-500/30", + dot: "bg-yellow-400" + }, + PICKING: { + label: "Picking", + color: "text-blue-400 border-blue-500/30", + dot: "bg-blue-400" + }, + PLAYING: { + label: "Live", + color: "text-green-400 border-green-500/30", + dot: "bg-green-400" + }, + FINISHED: { + label: "Finished", + color: "text-text-secondary border-border" + }, + CANCELLED: { label: "Cancelled", color: "text-red-400 border-red-500/30" } + }; + const liveStates = ["LOBBY", "ROLLING", "PICKING", "PLAYING"]; + const liveMatches = derived(() => data.matches.filter((m) => liveStates.includes(m.state))); + const recentMatches = derived(() => data.matches.filter((m) => !liveStates.includes(m.state))); + function teamDisplay(m) { + const p1 = m.participants[0]; + const p2 = m.participants[1]; + return { p1, p2 }; + } + function timeAgo(date) { + const d = new Date(date); + const diff = Date.now() - d.getTime(); + const mins = Math.floor(diff / 6e4); + if (mins < 1) return "just now"; + if (mins < 60) return `${mins}m ago`; + const hours = Math.floor(mins / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; + } + $$renderer2.push(`

Matches

${escape_html(data.matches.length)} match${escape_html(data.matches.length !== 1 ? "es" : "")} `); + if (liveMatches().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`· ${escape_html(liveMatches().length)} live`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

`); + { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (liveMatches().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Live Now

`); + const each_array_4 = ensure_array_like(liveMatches()); + for (let $$index_4 = 0, $$length = each_array_4.length; $$index_4 < $$length; $$index_4++) { + let m = each_array_4[$$index_4]; + const { p1, p2 } = teamDisplay(m); + const sc = stateConfig[m.state]; + $$renderer2.push(`
`); + if (p1?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(p1?.team.name ?? "?")}
${escape_html(p1?.score ?? 0)} vs ${escape_html(p2?.score ?? 0)}
${escape_html(p2?.team.name ?? "?")} `); + if (p2?.team.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (sc?.dot) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label ?? m.state)}`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + if (liveMatches().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Past Matches

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + const each_array_5 = ensure_array_like(recentMatches()); + if (each_array_5.length !== 0) { + $$renderer2.push(""); + for (let $$index_5 = 0, $$length = each_array_5.length; $$index_5 < $$length; $$index_5++) { + let m = each_array_5[$$index_5]; + const { p1, p2 } = teamDisplay(m); + const config = m.config; + const sc = stateConfig[m.state]; + $$renderer2.push(`
${escape_html(p1?.team.name ?? "?")} `); + if (m.state === "FINISHED") { + $$renderer2.push(""); + $$renderer2.push(` (p2?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p1?.score ?? 0)} - (p1?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p2?.score ?? 0)}`); + } else { + $$renderer2.push(""); + $$renderer2.push(`vs`); + } + $$renderer2.push(` ${escape_html(p2?.team.name ?? "?")}

${escape_html(m.name ? `${m.name} · ` : "")}BO${escape_html(config.bestOf)} `); + if (m.finishedAt) { + $$renderer2.push(""); + $$renderer2.push(`· ${escape_html(timeAgo(m.finishedAt))}`); + } else if (m.createdAt) { + $$renderer2.push(""); + $$renderer2.push(`· ${escape_html(timeAgo(m.createdAt))}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`

`); + if (m.state === "FINISHED" && m.winnerId) { + $$renderer2.push(""); + const winner = m.participants.find((p) => p.teamId === m.winnerId); + $$renderer2.push(`🏆 ${escape_html(winner?.team.name)}`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` ${escape_html(sc?.label ?? m.state)}`); + } + } else { + $$renderer2.push(""); + if (liveMatches().length === 0) { + $$renderer2.push(""); + $$renderer2.push(`

No matches yet. Create one to get started.

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(``); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/settings/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/settings/_page.server.ts.js new file mode 100644 index 0000000..0198d65 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/settings/_page.server.ts.js @@ -0,0 +1,37 @@ +import { redirect } from "@sveltejs/kit"; +import { a as auth } from "../../../../chunks/auth.js"; +import { d as db, p as playerRating, j as account } from "../../../../chunks/index2.js"; +import { eq, and } from "drizzle-orm"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const rating = await db.query.playerRating.findFirst({ + where: eq(playerRating.userId, locals.user.id) + }); + const osuAccount = await db.query.account.findFirst({ + where: and(eq(account.userId, locals.user.id), eq(account.providerId, "osu")) + }); + return { + profile: { + id: locals.user.id, + name: locals.user.name, + email: locals.user.email, + image: locals.user.image, + role: locals.user.role ?? "player", + createdAt: locals.user.createdAt + }, + rating: rating ? { elo: rating.elo, wins: rating.wins, losses: rating.losses } : { elo: 1e3, wins: 0, losses: 0 }, + hasOsuLinked: !!osuAccount + }; +}; +const actions = { + logout: async (event) => { + await auth.api.signOut({ + headers: event.request.headers + }); + redirect(302, "/"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/settings/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/settings/_page.svelte.js new file mode 100644 index 0000000..3b24951 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/settings/_page.svelte.js @@ -0,0 +1,48 @@ +import { b as attr, e as escape_html, c as attr_class, s as stringify, d as derived } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + const roleBadge = { + player: { label: "Player", color: "bg-surface-600 text-text-secondary" }, + referee: { label: "Referee", color: "bg-yellow-500/20 text-yellow-400" }, + admin: { label: "Admin", color: "bg-red-500/20 text-red-400" } + }; + const badge = derived(() => roleBadge[data.profile.role] ?? roleBadge.player); + const winRate = derived(() => data.rating.wins + data.rating.losses > 0 ? (data.rating.wins / (data.rating.wins + data.rating.losses) * 100).toFixed(1) : "—"); + $$renderer2.push(`

Settings

Your profile and account settings

`); + if (data.profile.image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
${escape_html(data.profile.name.charAt(0).toUpperCase())}
`); + } + $$renderer2.push(`

${escape_html(data.profile.name)}

${escape_html(badge().label)}

${escape_html(data.profile.email)}

Connected Accounts

🎮

osu!

`); + if (data.hasOsuLinked) { + $$renderer2.push(""); + $$renderer2.push(`Connected as ${escape_html(data.profile.name)}`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Not connected`); + } + $$renderer2.push(`

`); + if (data.hasOsuLinked) { + $$renderer2.push(""); + $$renderer2.push(`Linked`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Connect`); + } + $$renderer2.push(`

Ranked Stats

${escape_html(data.rating.elo)}

ELO Rating

${escape_html(data.rating.wins)}

Wins

${escape_html(data.rating.losses)}

Losses

${escape_html(winRate())}${escape_html(winRate() !== "—" ? "%" : "")}

Win Rate

Account

`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/teams/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/(app)/teams/_page.server.ts.js new file mode 100644 index 0000000..dbeb11a --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/teams/_page.server.ts.js @@ -0,0 +1,88 @@ +import { d as db, c as team, t as teamMember, u as user } from "../../../../chunks/index2.js"; +import { eq } from "drizzle-orm"; +import { redirect } from "@sveltejs/kit"; +const load = async ({ locals }) => { + if (!locals.user) redirect(302, "/"); + const teams = await db.query.team.findMany({ + with: { + members: true + }, + orderBy: (t, { desc }) => [desc(t.createdAt)] + }); + const teamsWithUsers = await Promise.all( + teams.map(async (t) => { + const membersWithUsers = await Promise.all( + t.members.map(async (m) => { + const u = await db.query.user.findFirst({ + where: eq(user.id, m.userId) + }); + return { ...m, user: u ? { id: u.id, name: u.name, image: u.image } : null }; + }) + ); + return { ...t, members: membersWithUsers }; + }) + ); + return { teams: teamsWithUsers, userId: locals.user.id }; +}; +const actions = { + create: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const name = form.get("name")?.toString()?.trim(); + if (!name) return { error: "Team name is required" }; + const [created] = await db.insert(team).values({ + name, + ownerId: locals.user.id, + isPersonal: false + }).returning(); + await db.insert(teamMember).values({ + teamId: created.id, + userId: locals.user.id, + role: "captain" + }); + return { success: true }; + }, + addMember: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const teamId = form.get("teamId")?.toString(); + const username = form.get("username")?.toString()?.trim(); + if (!teamId || !username) return { error: "Team and username are required" }; + const u = await db.query.user.findFirst({ + where: eq(user.name, username) + }); + if (!u) return { error: `User "${username}" not found. They need to log in first.` }; + const existing = await db.query.teamMember.findFirst({ + where: (m, { and, eq: eq2 }) => and(eq2(m.teamId, teamId), eq2(m.userId, u.id)) + }); + if (existing) return { error: `${username} is already on this team` }; + await db.insert(teamMember).values({ + teamId, + userId: u.id, + role: "member" + }); + return { success: true }; + }, + removeMember: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const memberId = form.get("memberId")?.toString(); + if (!memberId) return { error: "Missing member ID" }; + await db.delete(teamMember).where(eq(teamMember.id, memberId)); + return { success: true }; + }, + deleteTeam: async ({ request, locals }) => { + if (!locals.user) redirect(302, "/"); + const form = await request.formData(); + const teamId = form.get("teamId")?.toString(); + if (!teamId) return { error: "Missing team ID" }; + const t = await db.query.team.findFirst({ where: eq(team.id, teamId) }); + if (!t || t.ownerId !== locals.user.id) return { error: "Not authorized" }; + await db.delete(team).where(eq(team.id, teamId)); + return { success: true }; + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/(app)/teams/_page.svelte.js b/.svelte-kit/output/server/entries/pages/(app)/teams/_page.svelte.js new file mode 100644 index 0000000..e5203aa --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/(app)/teams/_page.svelte.js @@ -0,0 +1,138 @@ +import { e as escape_html, a as ensure_array_like, b as attr, c as attr_class, s as stringify, d as derived } from "../../../../chunks/index3.js"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + let expandedTeam = null; + let addingMemberTo = null; + let memberUsername = ""; + const myTeams = derived(() => data.teams.filter((t) => t.members.some((m) => m.userId === data.userId))); + const otherTeams = derived(() => data.teams.filter((t) => !t.members.some((m) => m.userId === data.userId))); + function isOwner(t) { + return t.ownerId === data.userId; + } + $$renderer2.push(`

Teams

${escape_html(data.teams.length)} team${escape_html(data.teams.length !== 1 ? "s" : "")}

`); + { + $$renderer2.push(""); + } + $$renderer2.push(` `); + { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (myTeams().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

My Teams

`); + const each_array = ensure_array_like(myTeams()); + for (let $$index_1 = 0, $$length = each_array.length; $$index_1 < $$length; $$index_1++) { + let t = each_array[$$index_1]; + $$renderer2.push(`
`); + if (expandedTeam === t.id) { + $$renderer2.push(""); + $$renderer2.push(`
`); + const each_array_1 = ensure_array_like(t.members); + for (let $$index = 0, $$length2 = each_array_1.length; $$index < $$length2; $$index++) { + let member = each_array_1[$$index]; + $$renderer2.push(`
`); + if (member.user?.image) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
`); + } + $$renderer2.push(` ${escape_html(member.user?.name ?? "Unknown")} ${escape_html(member.role)} `); + if (isOwner(t) && member.userId !== data.userId) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + if (isOwner(t) && !t.isPersonal) { + $$renderer2.push(""); + if (addingMemberTo === t.id) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + $$renderer2.push(``); + } + $$renderer2.push(``); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (isOwner(t) && !t.isPersonal) { + $$renderer2.push(""); + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (otherTeams().length > 0) { + $$renderer2.push(""); + $$renderer2.push(`

Other Teams

`); + const each_array_2 = ensure_array_like(otherTeams()); + for (let $$index_2 = 0, $$length = each_array_2.length; $$index_2 < $$length; $$index_2++) { + let t = each_array_2[$$index_2]; + $$renderer2.push(`
`); + if (t.avatarUrl) { + $$renderer2.push(""); + $$renderer2.push(``); + } else { + $$renderer2.push(""); + $$renderer2.push(`
${escape_html(t.name.charAt(0).toUpperCase())}
`); + } + $$renderer2.push(`
${escape_html(t.name)}

${escape_html(t.members.length)} member${escape_html(t.members.length !== 1 ? "s" : "")}

`); + } + $$renderer2.push(`
`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(` `); + if (data.teams.length === 0) { + $$renderer2.push(""); + $$renderer2.push(`

No teams yet. Your personal team is created when you sign in.

`); + } else { + $$renderer2.push(""); + } + $$renderer2.push(`
`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/_error.svelte.js b/.svelte-kit/output/server/entries/pages/_error.svelte.js new file mode 100644 index 0000000..85b4f7d --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/_error.svelte.js @@ -0,0 +1,25 @@ +import { e as escape_html } from "../../chunks/index3.js"; +import "clsx"; +import { p as page } from "../../chunks/index4.js"; +function _error($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + $$renderer2.push(`

${escape_html(page.status)}

`); + if (page.status === 404) { + $$renderer2.push(""); + $$renderer2.push(`Page not found`); + } else if (page.status === 403) { + $$renderer2.push(""); + $$renderer2.push(`Access denied`); + } else if (page.status === 401) { + $$renderer2.push(""); + $$renderer2.push(`Not logged in`); + } else { + $$renderer2.push(""); + $$renderer2.push(`Something went wrong`); + } + $$renderer2.push(`

${escape_html(page.error?.message ?? "An unexpected error occurred.")}

Back to Dashboard
`); + }); +} +export { + _error as default +}; diff --git a/.svelte-kit/output/server/entries/pages/demo/_page.svelte.js b/.svelte-kit/output/server/entries/pages/demo/_page.svelte.js new file mode 100644 index 0000000..1b9e0e2 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/demo/_page.svelte.js @@ -0,0 +1,31 @@ +import { b as attr } from "../../../chunks/index3.js"; +import { i as initial_base, b as base } from "../../../chunks/server.js"; +import { r as resolve_route } from "../../../chunks/routing.js"; +import "../../../chunks/url.js"; +import { try_get_request_store } from "@sveltejs/kit/internal/server"; +import "../../../chunks/root.js"; +function resolve(id, params) { + const resolved = resolve_route( + id, + /** @type {Record} */ + params + ); + { + const store = try_get_request_store(); + if (store && !store.state.prerendering?.fallback) { + const after_base = store.event.url.pathname.slice(initial_base.length); + const segments = after_base.split("/").slice(2); + const prefix = segments.map(() => "..").join("/") || "."; + return prefix + resolved; + } + } + return base + resolved; +} +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + $$renderer2.push(`better-auth`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/demo/better-auth/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/demo/better-auth/_page.server.ts.js new file mode 100644 index 0000000..8fa1822 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/demo/better-auth/_page.server.ts.js @@ -0,0 +1,20 @@ +import { redirect } from "@sveltejs/kit"; +import { a as auth } from "../../../../chunks/auth.js"; +const load = async (event) => { + if (!event.locals.user) { + return redirect(302, "/demo/better-auth/login"); + } + return { user: event.locals.user }; +}; +const actions = { + signOut: async (event) => { + await auth.api.signOut({ + headers: event.request.headers + }); + return redirect(302, "/demo/better-auth/login"); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/demo/better-auth/_page.svelte.js b/.svelte-kit/output/server/entries/pages/demo/better-auth/_page.svelte.js new file mode 100644 index 0000000..42126b3 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/demo/better-auth/_page.svelte.js @@ -0,0 +1,18 @@ +import { e as escape_html } from "../../../../chunks/index3.js"; +import "clsx"; +import "@sveltejs/kit/internal"; +import "../../../../chunks/url.js"; +import "../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../chunks/root.js"; +import "../../../../chunks/exports.js"; +import "../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { data } = $$props; + $$renderer2.push(`

Hi, ${escape_html(data.user.name)}!

Your user ID is ${escape_html(data.user.id)}.

`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/demo/better-auth/login/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/demo/better-auth/login/_page.server.ts.js new file mode 100644 index 0000000..2e2ee65 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/demo/better-auth/login/_page.server.ts.js @@ -0,0 +1,75 @@ +import { redirect, fail } from "@sveltejs/kit"; +import { a as auth } from "../../../../../chunks/auth.js"; +import "@better-auth/core/env"; +import "@better-auth/core/utils"; +import { APIError } from "better-call"; +import "@better-auth/core/api"; +const load = async (event) => { + if (event.locals.user) { + return redirect(302, "/demo/better-auth"); + } + return {}; +}; +const actions = { + signInEmail: async (event) => { + const formData = await event.request.formData(); + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + try { + await auth.api.signInEmail({ + body: { + email, + password, + callbackURL: "/auth/verification-success" + } + }); + } catch (error) { + if (error instanceof APIError) { + return fail(400, { message: error.message || "Signin failed" }); + } + return fail(500, { message: "Unexpected error" }); + } + return redirect(302, "/demo/better-auth"); + }, + signUpEmail: async (event) => { + const formData = await event.request.formData(); + const email = formData.get("email")?.toString() ?? ""; + const password = formData.get("password")?.toString() ?? ""; + const name = formData.get("name")?.toString() ?? ""; + try { + await auth.api.signUpEmail({ + body: { + email, + password, + name, + callbackURL: "/auth/verification-success" + } + }); + } catch (error) { + if (error instanceof APIError) { + return fail(400, { message: error.message || "Registration failed" }); + } + return fail(500, { message: "Unexpected error" }); + } + return redirect(302, "/demo/better-auth"); + }, + signInSocial: async (event) => { + const formData = await event.request.formData(); + const provider = formData.get("provider")?.toString() ?? "osu"; + const callbackURL = formData.get("callbackURL")?.toString() ?? "/demo/better-auth"; + const result = await auth.api.signInSocial({ + body: { + provider, + callbackURL + } + }); + if (result.url) { + return redirect(302, result.url); + } + return fail(400, { message: "Social sign-in failed" }); + } +}; +export { + actions, + load +}; diff --git a/.svelte-kit/output/server/entries/pages/demo/better-auth/login/_page.svelte.js b/.svelte-kit/output/server/entries/pages/demo/better-auth/login/_page.svelte.js new file mode 100644 index 0000000..c17dfef --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/demo/better-auth/login/_page.svelte.js @@ -0,0 +1,18 @@ +import { e as escape_html } from "../../../../../chunks/index3.js"; +import "clsx"; +import "@sveltejs/kit/internal"; +import "../../../../../chunks/url.js"; +import "../../../../../chunks/utils.js"; +import "@sveltejs/kit/internal/server"; +import "../../../../../chunks/root.js"; +import "../../../../../chunks/exports.js"; +import "../../../../../chunks/state.svelte.js"; +function _page($$renderer, $$props) { + $$renderer.component(($$renderer2) => { + let { form } = $$props; + $$renderer2.push(`

Login

${escape_html(form?.message ?? "")}


`); + }); +} +export { + _page as default +}; diff --git a/.svelte-kit/output/server/entries/pages/login/_page.server.ts.js b/.svelte-kit/output/server/entries/pages/login/_page.server.ts.js new file mode 100644 index 0000000..a8c7c75 --- /dev/null +++ b/.svelte-kit/output/server/entries/pages/login/_page.server.ts.js @@ -0,0 +1,16 @@ +import { redirect } from "@sveltejs/kit"; +import { a as auth } from "../../../chunks/auth.js"; +const load = async (event) => { + if (event.locals.user) redirect(302, "/"); + const result = await auth.api.signInSocial({ + body: { + provider: "osu", + callbackURL: "/" + } + }); + if (result.url) redirect(302, result.url); + return {}; +}; +export { + load +}; diff --git a/.svelte-kit/output/server/index.js b/.svelte-kit/output/server/index.js new file mode 100644 index 0000000..fb8a806 --- /dev/null +++ b/.svelte-kit/output/server/index.js @@ -0,0 +1,3698 @@ +import { D as DEV } from "./chunks/false.js"; +import { json, text, error } from "@sveltejs/kit"; +import { Redirect, SvelteKitError, ActionFailure, HttpError } from "@sveltejs/kit/internal"; +import { with_request_store, merge_tracing, try_get_request_store } from "@sveltejs/kit/internal/server"; +import { a as assets, b as base, c as app_dir, r as relative, o as override, d as reset } from "./chunks/server.js"; +import { E as ENDPOINT_METHODS, P as PAGE_METHODS, n as negotiate, m as method_not_allowed, h as handle_error_and_jsonify, g as get_status, i as is_form_content_type, a as normalize_error, b as get_global_name, s as serialize_uses, c as clarify_devalue_error, d as get_node_type, e as escape_html, S as SVELTE_KIT_ASSETS, f as create_remote_key, j as static_error_page, r as redirect_response, p as parse_remote_arg, k as stringify, l as deserialize_binary_form, o as has_prerendered_path, T as TRAILING_SLASH_PARAM, I as INVALIDATED_PARAM, q as handle_fatal_error, M as MUTATIVE_METHODS, t as format_server_error } from "./chunks/shared.js"; +import * as devalue from "devalue"; +import { m as make_trackable, d as disable_search, S as SCHEME, n as normalize_path, r as resolve, a as decode_pathname } from "./chunks/url.js"; +import { b as base64_encode, t as text_decoder, a as text_encoder, g as get_relative_path } from "./chunks/utils.js"; +import { w as writable, r as readable } from "./chunks/index.js"; +import { p as public_env, s as set_private_env, a as set_public_env } from "./chunks/shared-server.js"; +import { f as find_route } from "./chunks/routing.js"; +import { v as validate_layout_server_exports, a as validate_layout_exports, b as validate_page_server_exports, c as validate_page_exports, d as validate_server_exports } from "./chunks/exports.js"; +import { parse, serialize } from "cookie"; +import * as set_cookie_parser from "set-cookie-parser"; +import { r as read_implementation, o as options, g as get_hooks, s as set_read_implementation } from "./chunks/internal.js"; +function with_resolvers() { + let resolve2; + let reject; + const promise = new Promise((res, rej) => { + resolve2 = res; + reject = rej; + }); + return { promise, resolve: resolve2, reject }; +} +const NULL_BODY_STATUS = [101, 103, 204, 205, 304]; +const IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer; +async function render_endpoint(event, event_state, mod, state) { + const method = ( + /** @type {import('types').HttpMethod} */ + event.request.method + ); + let handler = mod[method] || mod.fallback; + if (method === "HEAD" && !mod.HEAD && mod.GET) { + handler = mod.GET; + } + if (!handler) { + return method_not_allowed(mod, method); + } + const prerender = mod.prerender ?? state.prerender_default; + if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) { + throw new Error("Cannot prerender endpoints that have mutative methods"); + } + if (state.prerendering && !state.prerendering.inside_reroute && !prerender) { + if (state.depth > 0) { + throw new Error(`${event.route.id} is not prerenderable`); + } else { + return new Response(void 0, { status: 204 }); + } + } + try { + event_state.allows_commands = true; + const response = await with_request_store( + { event, state: event_state }, + () => handler( + /** @type {import('@sveltejs/kit').RequestEvent>} */ + event + ) + ); + if (!(response instanceof Response)) { + throw new Error( + `Invalid response from route ${event.url.pathname}: handler should return a Response object` + ); + } + if (state.prerendering && (!state.prerendering.inside_reroute || prerender)) { + const cloned = new Response(response.clone().body, { + status: response.status, + statusText: response.statusText, + headers: new Headers(response.headers) + }); + cloned.headers.set("x-sveltekit-prerender", String(prerender)); + if (state.prerendering.inside_reroute && prerender) { + cloned.headers.set( + "x-sveltekit-routeid", + encodeURI( + /** @type {string} */ + event.route.id + ) + ); + state.prerendering.dependencies.set(event.url.pathname, { response: cloned, body: null }); + } else { + return cloned; + } + } + return response; + } catch (e) { + if (e instanceof Redirect) { + return new Response(void 0, { + status: e.status, + headers: { location: e.location } + }); + } + throw e; + } +} +function is_endpoint_request(event) { + const { method, headers: headers2 } = event.request; + if (ENDPOINT_METHODS.includes(method) && !PAGE_METHODS.includes(method)) { + return true; + } + if (method === "POST" && headers2.get("x-sveltekit-action") === "true") return false; + const accept = event.request.headers.get("accept") ?? "*/*"; + return negotiate(accept, ["*", "text/html"]) !== "text/html"; +} +function compact(arr) { + return arr.filter( + /** @returns {val is NonNullable} */ + (val) => val != null + ); +} +const DATA_SUFFIX = "/__data.json"; +const HTML_DATA_SUFFIX = ".html__data.json"; +function has_data_suffix(pathname) { + return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX); +} +function add_data_suffix(pathname) { + if (pathname.endsWith(".html")) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX); + return pathname.replace(/\/$/, "") + DATA_SUFFIX; +} +function strip_data_suffix(pathname) { + if (pathname.endsWith(HTML_DATA_SUFFIX)) { + return pathname.slice(0, -HTML_DATA_SUFFIX.length) + ".html"; + } + return pathname.slice(0, -DATA_SUFFIX.length); +} +const ROUTE_SUFFIX = "/__route.js"; +function has_resolution_suffix(pathname) { + return pathname.endsWith(ROUTE_SUFFIX); +} +function add_resolution_suffix(pathname) { + return pathname.replace(/\/$/, "") + ROUTE_SUFFIX; +} +function strip_resolution_suffix(pathname) { + return pathname.slice(0, -ROUTE_SUFFIX.length); +} +const noop_span = { + spanContext() { + return noop_span_context; + }, + setAttribute() { + return this; + }, + setAttributes() { + return this; + }, + addEvent() { + return this; + }, + setStatus() { + return this; + }, + updateName() { + return this; + }, + end() { + return this; + }, + isRecording() { + return false; + }, + recordException() { + return this; + }, + addLink() { + return this; + }, + addLinks() { + return this; + } +}; +const noop_span_context = { + traceId: "", + spanId: "", + traceFlags: 0 +}; +async function record_span({ name, attributes, fn }) { + { + return fn(noop_span); + } +} +function is_action_json_request(event) { + const accept = negotiate(event.request.headers.get("accept") ?? "*/*", [ + "application/json", + "text/html" + ]); + return accept === "application/json" && event.request.method === "POST"; +} +async function handle_action_json_request(event, event_state, options2, server) { + const actions = server?.actions; + if (!actions) { + const no_actions_error = new SvelteKitError( + 405, + "Method Not Allowed", + `POST method not allowed. No form actions exist for ${"this page"}` + ); + return action_json( + { + type: "error", + error: await handle_error_and_jsonify(event, event_state, options2, no_actions_error) + }, + { + status: no_actions_error.status, + headers: { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: "GET" + } + } + ); + } + check_named_default_separate(actions); + try { + const data = await call_action(event, event_state, actions); + if (DEV) ; + if (data instanceof ActionFailure) { + return action_json({ + type: "failure", + status: data.status, + // @ts-expect-error we assign a string to what is supposed to be an object. That's ok + // because we don't use the object outside, and this way we have better code navigation + // through knowing where the related interface is used. + data: stringify_action_response( + data.data, + /** @type {string} */ + event.route.id, + options2.hooks.transport + ) + }); + } else { + return action_json({ + type: "success", + status: data ? 200 : 204, + // @ts-expect-error see comment above + data: stringify_action_response( + data, + /** @type {string} */ + event.route.id, + options2.hooks.transport + ) + }); + } + } catch (e) { + const err = normalize_error(e); + if (err instanceof Redirect) { + return action_json_redirect(err); + } + return action_json( + { + type: "error", + error: await handle_error_and_jsonify( + event, + event_state, + options2, + check_incorrect_fail_use(err) + ) + }, + { + status: get_status(err) + } + ); + } +} +function check_incorrect_fail_use(error2) { + return error2 instanceof ActionFailure ? new Error('Cannot "throw fail()". Use "return fail()"') : error2; +} +function action_json_redirect(redirect) { + return action_json({ + type: "redirect", + status: redirect.status, + location: redirect.location + }); +} +function action_json(data, init2) { + return json(data, init2); +} +function is_action_request(event) { + return event.request.method === "POST"; +} +async function handle_action_request(event, event_state, server) { + const actions = server?.actions; + if (!actions) { + event.setHeaders({ + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: "GET" + }); + return { + type: "error", + error: new SvelteKitError( + 405, + "Method Not Allowed", + `POST method not allowed. No form actions exist for ${"this page"}` + ) + }; + } + check_named_default_separate(actions); + try { + const data = await call_action(event, event_state, actions); + if (DEV) ; + if (data instanceof ActionFailure) { + return { + type: "failure", + status: data.status, + data: data.data + }; + } else { + return { + type: "success", + status: 200, + // @ts-expect-error this will be removed upon serialization, so `undefined` is the same as omission + data + }; + } + } catch (e) { + const err = normalize_error(e); + if (err instanceof Redirect) { + return { + type: "redirect", + status: err.status, + location: err.location + }; + } + return { + type: "error", + error: check_incorrect_fail_use(err) + }; + } +} +function check_named_default_separate(actions) { + if (actions.default && Object.keys(actions).length > 1) { + throw new Error( + "When using named actions, the default action cannot be used. See the docs for more info: https://svelte.dev/docs/kit/form-actions#named-actions" + ); + } +} +async function call_action(event, event_state, actions) { + const url = new URL(event.request.url); + let name = "default"; + for (const param of url.searchParams) { + if (param[0].startsWith("/")) { + name = param[0].slice(1); + if (name === "default") { + throw new Error('Cannot use reserved action name "default"'); + } + break; + } + } + const action = actions[name]; + if (!action) { + throw new SvelteKitError(404, "Not Found", `No action with name '${name}' found`); + } + if (!is_form_content_type(event.request)) { + throw new SvelteKitError( + 415, + "Unsupported Media Type", + `Form actions expect form-encoded data — received ${event.request.headers.get( + "content-type" + )}` + ); + } + return record_span({ + name: "sveltekit.form_action", + attributes: { + "http.route": event.route.id || "unknown" + }, + fn: async (current2) => { + const traced_event = merge_tracing(event, current2); + event_state.allows_commands = true; + const result = await with_request_store( + { event: traced_event, state: event_state }, + () => action(traced_event) + ); + if (result instanceof ActionFailure) { + current2.setAttributes({ + "sveltekit.form_action.result.type": "failure", + "sveltekit.form_action.result.status": result.status + }); + } + return result; + } + }); +} +function validate_action_return(data) { + if (data instanceof Redirect) { + throw new Error("Cannot `return redirect(...)` — use `redirect(...)` instead"); + } + if (data instanceof HttpError) { + throw new Error("Cannot `return error(...)` — use `error(...)` or `return fail(...)` instead"); + } +} +function uneval_action_response(data, route_id, transport) { + const replacer = (thing) => { + for (const key2 in transport) { + const encoded = transport[key2].encode(thing); + if (encoded) { + return `app.decode('${key2}', ${devalue.uneval(encoded, replacer)})`; + } + } + }; + return try_serialize(data, (value) => devalue.uneval(value, replacer), route_id); +} +function stringify_action_response(data, route_id, transport) { + const encoders = Object.fromEntries( + Object.entries(transport).map(([key2, value]) => [key2, value.encode]) + ); + return try_serialize(data, (value) => devalue.stringify(value, encoders), route_id); +} +function try_serialize(data, fn, route_id) { + try { + return fn(data); + } catch (e) { + const error2 = ( + /** @type {any} */ + e + ); + if (data instanceof Response) { + throw new Error( + `Data returned from action inside ${route_id} is not serializable. Form actions need to return plain objects or fail(). E.g. return { success: true } or return fail(400, { message: "invalid" });`, + { cause: e } + ); + } + if ("path" in error2) { + let message = `Data returned from action inside ${route_id} is not serializable: ${error2.message}`; + if (error2.path !== "") message += ` (data.${error2.path})`; + throw new Error(message, { cause: e }); + } + throw error2; + } +} +function create_async_iterator() { + let resolved = -1; + let returned = -1; + const deferred = []; + return { + iterate: (transform = (x) => x) => { + return { + [Symbol.asyncIterator]() { + return { + next: async () => { + const next = deferred[++returned]; + if (!next) return { value: null, done: true }; + const value = await next.promise; + return { value: transform(value), done: false }; + } + }; + } + }; + }, + add: (promise) => { + deferred.push(with_resolvers()); + void promise.then((value) => { + deferred[++resolved].resolve(value); + }); + } + }; +} +function server_data_serializer(event, event_state, options2) { + let promise_id = 1; + let max_nodes = -1; + const iterator = create_async_iterator(); + const global = get_global_name(options2); + function get_replacer(index) { + return function replacer(thing) { + if (typeof thing?.then === "function") { + const id = promise_id++; + const promise = thing.then( + /** @param {any} data */ + (data) => ({ data }) + ).catch( + /** @param {any} error */ + async (error2) => ({ + error: await handle_error_and_jsonify(event, event_state, options2, error2) + }) + ).then( + /** + * @param {{data: any; error: any}} result + */ + async ({ data, error: error2 }) => { + let str; + try { + str = devalue.uneval(error2 ? [, error2] : [data], replacer); + } catch { + error2 = await handle_error_and_jsonify( + event, + event_state, + options2, + new Error(`Failed to serialize promise while rendering ${event.route.id}`) + ); + str = devalue.uneval([, error2], replacer); + } + return { + index, + str: `${global}.resolve(${id}, ${str.includes("app.decode") ? `(app) => ${str}` : `() => ${str}`})` + }; + } + ); + iterator.add(promise); + return `${global}.defer(${id})`; + } else { + for (const key2 in options2.hooks.transport) { + const encoded = options2.hooks.transport[key2].encode(thing); + if (encoded) { + return `app.decode('${key2}', ${devalue.uneval(encoded, replacer)})`; + } + } + } + }; + } + const strings = ( + /** @type {string[]} */ + [] + ); + return { + set_max_nodes(i) { + max_nodes = i; + }, + add_node(i, node) { + try { + if (!node) { + strings[i] = "null"; + return; + } + const payload = { type: "data", data: node.data, uses: serialize_uses(node) }; + if (node.slash) payload.slash = node.slash; + strings[i] = devalue.uneval(payload, get_replacer(i)); + } catch (e) { + e.path = e.path.slice(1); + throw new Error(clarify_devalue_error( + event, + /** @type {any} */ + e + ), { cause: e }); + } + }, + get_data(csp) { + const open = ``; + const close = `<\/script> +`; + return { + data: `[${compact(max_nodes > -1 ? strings.slice(0, max_nodes) : strings).join(",")}]`, + chunks: promise_id > 1 ? iterator.iterate(({ index, str }) => { + if (max_nodes > -1 && index >= max_nodes) { + return ""; + } + return open + str + close; + }) : null + }; + } + }; +} +function server_data_serializer_json(event, event_state, options2) { + let promise_id = 1; + const iterator = create_async_iterator(); + const reducers = { + ...Object.fromEntries( + Object.entries(options2.hooks.transport).map(([key2, value]) => [key2, value.encode]) + ), + /** @param {any} thing */ + Promise: (thing) => { + if (typeof thing?.then !== "function") { + return; + } + const id = promise_id++; + let key2 = "data"; + const promise = thing.catch( + /** @param {any} e */ + async (e) => { + key2 = "error"; + return handle_error_and_jsonify( + event, + event_state, + options2, + /** @type {any} */ + e + ); + } + ).then( + /** @param {any} value */ + async (value) => { + let str; + try { + str = devalue.stringify(value, reducers); + } catch { + const error2 = await handle_error_and_jsonify( + event, + event_state, + options2, + new Error(`Failed to serialize promise while rendering ${event.route.id}`) + ); + key2 = "error"; + str = devalue.stringify(error2, reducers); + } + return `{"type":"chunk","id":${id},"${key2}":${str}} +`; + } + ); + iterator.add(promise); + return id; + } + }; + const strings = ( + /** @type {string[]} */ + [] + ); + return { + add_node(i, node) { + try { + if (!node) { + strings[i] = "null"; + return; + } + if (node.type === "error" || node.type === "skip") { + strings[i] = JSON.stringify(node); + return; + } + strings[i] = `{"type":"data","data":${devalue.stringify(node.data, reducers)},"uses":${JSON.stringify( + serialize_uses(node) + )}${node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ""}}`; + } catch (e) { + e.path = "data" + e.path; + throw new Error(clarify_devalue_error( + event, + /** @type {any} */ + e + ), { cause: e }); + } + }, + get_data() { + return { + data: `{"type":"data","nodes":[${strings.join(",")}]} +`, + chunks: promise_id > 1 ? iterator.iterate() : null + }; + } + }; +} +async function load_server_data({ event, event_state, state, node, parent }) { + if (!node?.server) return null; + let is_tracking = true; + const uses = { + dependencies: /* @__PURE__ */ new Set(), + params: /* @__PURE__ */ new Set(), + parent: false, + route: false, + url: false, + search_params: /* @__PURE__ */ new Set() + }; + const load = node.server.load; + const slash = node.server.trailingSlash; + if (!load) { + return { type: "data", data: null, uses, slash }; + } + const url = make_trackable( + event.url, + () => { + if (is_tracking) { + uses.url = true; + } + }, + (param) => { + if (is_tracking) { + uses.search_params.add(param); + } + } + ); + if (state.prerendering) { + disable_search(url); + } + const result = await record_span({ + name: "sveltekit.load", + attributes: { + "sveltekit.load.node_id": node.server_id || "unknown", + "sveltekit.load.node_type": get_node_type(node.server_id), + "http.route": event.route.id || "unknown" + }, + fn: async (current2) => { + const traced_event = merge_tracing(event, current2); + const result2 = await with_request_store( + { event: traced_event, state: event_state }, + () => load.call(null, { + ...traced_event, + fetch: (info, init2) => { + new URL(info instanceof Request ? info.url : info, event.url); + return event.fetch(info, init2); + }, + /** @param {string[]} deps */ + depends: (...deps) => { + for (const dep of deps) { + const { href } = new URL(dep, event.url); + uses.dependencies.add(href); + } + }, + params: new Proxy(event.params, { + get: (target, key2) => { + if (is_tracking) { + uses.params.add(key2); + } + return target[ + /** @type {string} */ + key2 + ]; + } + }), + parent: async () => { + if (is_tracking) { + uses.parent = true; + } + return parent(); + }, + route: new Proxy(event.route, { + get: (target, key2) => { + if (is_tracking) { + uses.route = true; + } + return target[ + /** @type {'id'} */ + key2 + ]; + } + }), + url, + untrack(fn) { + is_tracking = false; + try { + return fn(); + } finally { + is_tracking = true; + } + } + }) + ); + return result2; + } + }); + return { + type: "data", + data: result ?? null, + uses, + slash + }; +} +async function load_data({ + event, + event_state, + fetched, + node, + parent, + server_data_promise, + state, + resolve_opts, + csr +}) { + const server_data_node = await server_data_promise; + const load = node?.universal?.load; + if (!load) { + return server_data_node?.data ?? null; + } + const result = await record_span({ + name: "sveltekit.load", + attributes: { + "sveltekit.load.node_id": node.universal_id || "unknown", + "sveltekit.load.node_type": get_node_type(node.universal_id), + "http.route": event.route.id || "unknown" + }, + fn: async (current2) => { + const traced_event = merge_tracing(event, current2); + return await with_request_store( + { event: traced_event, state: event_state }, + () => load.call(null, { + url: event.url, + params: event.params, + data: server_data_node?.data ?? null, + route: event.route, + fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts), + setHeaders: event.setHeaders, + depends: () => { + }, + parent, + untrack: (fn) => fn(), + tracing: traced_event.tracing + }) + ); + } + }); + return result ?? null; +} +function create_universal_fetch(event, state, fetched, csr, resolve_opts) { + const universal_fetch = async (input, init2) => { + const cloned_body = input instanceof Request && input.body ? input.clone().body : null; + const cloned_headers = input instanceof Request && [...input.headers].length ? new Headers(input.headers) : init2?.headers; + let response = await event.fetch(input, init2); + const url = new URL(input instanceof Request ? input.url : input, event.url); + const same_origin = url.origin === event.url.origin; + let dependency; + if (same_origin) { + if (state.prerendering) { + dependency = { response, body: null }; + state.prerendering.dependencies.set(url.pathname, dependency); + } + } else if (url.protocol === "https:" || url.protocol === "http:") { + const mode = input instanceof Request ? input.mode : init2?.mode ?? "cors"; + if (mode === "no-cors") { + response = new Response("", { + status: response.status, + statusText: response.statusText, + headers: response.headers + }); + } else { + const acao = response.headers.get("access-control-allow-origin"); + if (!acao || acao !== event.url.origin && acao !== "*") { + throw new Error( + `CORS error: ${acao ? "Incorrect" : "No"} 'Access-Control-Allow-Origin' header is present on the requested resource` + ); + } + } + } + let teed_body; + const proxy = new Proxy(response, { + get(response2, key2, receiver) { + async function push_fetched(body2, is_b64) { + const status_number = Number(response2.status); + if (isNaN(status_number)) { + throw new Error( + `response.status is not a number. value: "${response2.status}" type: ${typeof response2.status}` + ); + } + fetched.push({ + url: same_origin ? url.href.slice(event.url.origin.length) : url.href, + method: event.request.method, + request_body: ( + /** @type {string | ArrayBufferView | undefined} */ + input instanceof Request && cloned_body ? await stream_to_string(cloned_body) : init2?.body + ), + request_headers: cloned_headers, + response_body: body2, + response: response2, + is_b64 + }); + } + if (key2 === "body") { + if (response2.body === null) { + return null; + } + if (teed_body) { + return teed_body; + } + const [a, b] = response2.body.tee(); + void (async () => { + let result = new Uint8Array(); + for await (const chunk of a) { + const combined = new Uint8Array(result.length + chunk.length); + combined.set(result, 0); + combined.set(chunk, result.length); + result = combined; + } + if (dependency) { + dependency.body = new Uint8Array(result); + } + void push_fetched(base64_encode(result), true); + })(); + return teed_body = b; + } + if (key2 === "arrayBuffer") { + return async () => { + const buffer = await response2.arrayBuffer(); + const bytes = new Uint8Array(buffer); + if (dependency) { + dependency.body = bytes; + } + if (buffer instanceof ArrayBuffer) { + await push_fetched(base64_encode(bytes), true); + } + return buffer; + }; + } + async function text2() { + const body2 = await response2.text(); + if (body2 === "" && NULL_BODY_STATUS.includes(response2.status)) { + await push_fetched(void 0, false); + return void 0; + } + if (!body2 || typeof body2 === "string") { + await push_fetched(body2, false); + } + if (dependency) { + dependency.body = body2; + } + return body2; + } + if (key2 === "text") { + return text2; + } + if (key2 === "json") { + return async () => { + const body2 = await text2(); + return body2 ? JSON.parse(body2) : void 0; + }; + } + const value = Reflect.get(response2, key2, response2); + if (value instanceof Function) { + return Object.defineProperties( + /** + * @this {any} + */ + function() { + return Reflect.apply(value, this === receiver ? response2 : this, arguments); + }, + { + name: { value: value.name }, + length: { value: value.length } + } + ); + } + return value; + } + }); + if (csr) { + const get = response.headers.get; + response.headers.get = (key2) => { + const lower = key2.toLowerCase(); + const value = get.call(response.headers, lower); + if (value && !lower.startsWith("x-sveltekit-")) { + const included = resolve_opts.filterSerializedResponseHeaders(lower, value); + if (!included) { + throw new Error( + `Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://svelte.dev/docs/kit/hooks#Server-hooks-handle (at ${event.route.id})` + ); + } + } + return value; + }; + } + return proxy; + }; + return (input, init2) => { + const response = universal_fetch(input, init2); + response.catch(() => { + }); + return response; + }; +} +async function stream_to_string(stream) { + let result = ""; + const reader = stream.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) { + break; + } + result += text_decoder.decode(value); + } + return result; +} +function hash(...values) { + let hash2 = 5381; + for (const value of values) { + if (typeof value === "string") { + let i = value.length; + while (i) hash2 = hash2 * 33 ^ value.charCodeAt(--i); + } else if (ArrayBuffer.isView(value)) { + const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength); + let i = buffer.length; + while (i) hash2 = hash2 * 33 ^ buffer[--i]; + } else { + throw new TypeError("value must be a string or TypedArray"); + } + } + return (hash2 >>> 0).toString(36); +} +const replacements = { + "<": "\\u003C", + "\u2028": "\\u2028", + "\u2029": "\\u2029" +}; +const pattern = new RegExp(`[${Object.keys(replacements).join("")}]`, "g"); +function serialize_data(fetched, filter, prerendering = false) { + const headers2 = {}; + let cache_control = null; + let age = null; + let varyAny = false; + for (const [key2, value] of fetched.response.headers) { + if (filter(key2, value)) { + headers2[key2] = value; + } + if (key2 === "cache-control") cache_control = value; + else if (key2 === "age") age = value; + else if (key2 === "vary" && value.trim() === "*") varyAny = true; + } + const payload = { + status: fetched.response.status, + statusText: fetched.response.statusText, + headers: headers2, + body: fetched.response_body + }; + const safe_payload = JSON.stringify(payload).replace(pattern, (match) => replacements[match]); + const attrs = [ + 'type="application/json"', + "data-sveltekit-fetched", + `data-url="${escape_html(fetched.url, true)}"` + ]; + if (fetched.is_b64) { + attrs.push("data-b64"); + } + if (fetched.request_headers || fetched.request_body) { + const values = []; + if (fetched.request_headers) { + values.push([...new Headers(fetched.request_headers)].join(",")); + } + if (fetched.request_body) { + values.push(fetched.request_body); + } + attrs.push(`data-hash="${hash(...values)}"`); + } + if (!prerendering && fetched.method === "GET" && cache_control && !varyAny) { + const match = /s-maxage=(\d+)/g.exec(cache_control) ?? /max-age=(\d+)/g.exec(cache_control); + if (match) { + const ttl = +match[1] - +(age ?? "0"); + attrs.push(`data-ttl="${ttl}"`); + } + } + return `