feat: implement leaderboard page with top players, teams, and high scores
- Added a new leaderboard route with server-side loading for top players, teams, and recent high scores. - Created a new Svelte component for the leaderboard UI, including tabs for players, teams, and scores. - Implemented ELO ranking display and win/loss statistics for players. - Added functionality to display team wins and member counts. - Included recent high scores with player context and map details. - Enhanced team management features in the teams route, including renaming and member management with permission checks.
This commit is contained in:
@@ -1,661 +0,0 @@
|
||||
{
|
||||
".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"
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,69 +0,0 @@
|
||||
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
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,350 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,70 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
const DEV = false;
|
||||
export {
|
||||
DEV as D
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,302 +0,0 @@
|
||||
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
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,224 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,307 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,127 +0,0 @@
|
||||
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 }) => '<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width, initial-scale=1" />\n <link rel="preconnect" href="https://fonts.googleapis.com" />\n <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\n <link\n href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700;800&family=Geist+Mono:wght@400;500&display=swap"\n rel="stylesheet"\n />\n <title>Vash Esports</title>\n ' + head + '\n </head>\n <body data-sveltekit-preload-data="hover">\n <div style="display: contents">' + body + "</div>\n </body>\n</html>\n",
|
||||
error: ({ status, message }) => '<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <title>' + message + `</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
--bg: white;
|
||||
--fg: #222;
|
||||
--divider: #ccc;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Open Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 32rem;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-weight: 200;
|
||||
font-size: 3rem;
|
||||
line-height: 1;
|
||||
position: relative;
|
||||
top: -0.05rem;
|
||||
}
|
||||
|
||||
.message {
|
||||
border-left: 1px solid var(--divider);
|
||||
padding: 0 0 0 1rem;
|
||||
margin: 0 0 0 1rem;
|
||||
min-height: 2.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message h1 {
|
||||
font-weight: 400;
|
||||
font-size: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
--bg: #222;
|
||||
--fg: #ddd;
|
||||
--divider: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error">
|
||||
<span class="status">` + status + '</span>\n <div class="message">\n <h1>' + message + "</h1>\n </div>\n </div>\n </body>\n</html>\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
|
||||
};
|
||||
@@ -1,155 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,448 +0,0 @@
|
||||
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 <slot> (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 <slot> (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
|
||||
};
|
||||
@@ -1,258 +0,0 @@
|
||||
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<Output>} */
|
||||
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<Output>} */
|
||||
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
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,85 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,789 +0,0 @@
|
||||
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<number>} */
|
||||
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<Uint8Array<ArrayBuffer> | undefined>} */
|
||||
#get_chunk;
|
||||
/** @type {number} */
|
||||
#offset;
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} type
|
||||
* @param {number} size
|
||||
* @param {number} last_modified
|
||||
* @param {(index: number) => Promise<Uint8Array<ArrayBuffer> | 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
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
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")
|
||||
});
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import "clsx";
|
||||
function Layout($$renderer, $$props) {
|
||||
let { children } = $$props;
|
||||
children($$renderer);
|
||||
$$renderer.push(`<!---->`);
|
||||
}
|
||||
export {
|
||||
Layout as default
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,97 +0,0 @@
|
||||
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(`<aside class="fixed top-0 left-0 z-40 flex h-full w-56 flex-col border-r border-border bg-surface-800"><a href="/" class="flex items-center gap-2.5 px-5 py-5"><div class="font-800 flex h-8 w-8 items-center justify-center rounded-md bg-accent text-sm text-surface-900">V</div> <span class="font-700 text-base tracking-tight text-text-primary">Vash Esports</span></a> <nav class="mt-2 flex flex-1 flex-col gap-0.5 px-3"><!--[-->`);
|
||||
const each_array = ensure_array_like(nav);
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let item = each_array[$$index];
|
||||
$$renderer2.push(`<a${attr("href", item.href)}${attr_class(`font-500 flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors ${stringify(isActive(item.href) ? "bg-accent-dim text-accent" : "text-text-secondary hover:bg-surface-700 hover:text-text-primary")}`)}><span class="text-base">${escape_html(item.icon)}</span> ${escape_html(item.label)}</a>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (isAdmin()) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-4 mb-1 px-3"><p class="text-[10px] font-600 text-text-secondary uppercase tracking-widest">Staff</p></div> <a href="/admin"${attr_class(`font-500 flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors ${stringify(isActive("/admin") ? "bg-red-500/10 text-red-400" : "text-text-secondary hover:bg-surface-700 hover:text-text-primary")}`)}><span class="text-base">⚡</span> Admin</a>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></nav> <div class="border-t border-border p-3"><a href="/settings"${attr_class(`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 ${stringify(isActive("/settings") ? "bg-accent-dim text-accent" : "")}`)}><span class="text-base">⚙</span> Settings</a></div></aside>`);
|
||||
});
|
||||
}
|
||||
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(`<header class="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-border bg-surface-900/80 px-6 backdrop-blur-md"><div></div> <div class="flex items-center gap-4">`);
|
||||
if (user()) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="flex items-center gap-3">`);
|
||||
if (badge()) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span${attr_class(`rounded px-1.5 py-0.5 text-[10px] font-700 ${stringify(badge().color)}`)}>${escape_html(badge().label)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (user().image) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", user().image)}${attr("alt", user().name)} class="h-7 w-7 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span class="font-500 text-sm text-text-primary">${escape_html(user().name)}</span> <form method="post" action="/settings?/logout"><button type="submit" class="rounded-md border border-border px-2.5 py-1 text-xs text-text-secondary transition-colors hover:border-red-500/30 hover:text-red-400">Logout</button></form></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<a href="/login" class="font-600 rounded-md bg-accent px-3.5 py-1.5 text-sm text-surface-900 transition-colors hover:bg-accent-hover">Sign in with osu!</a>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></header>`);
|
||||
});
|
||||
}
|
||||
function _layout($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
let { children } = $$props;
|
||||
const user = derived(() => page.data?.user);
|
||||
if (user()) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="flex min-h-dvh">`);
|
||||
Sidebar($$renderer2);
|
||||
$$renderer2.push(`<!----> <div class="ml-56 flex flex-1 flex-col">`);
|
||||
Header($$renderer2);
|
||||
$$renderer2.push(`<!----> <main class="flex-1 p-6">`);
|
||||
children($$renderer2);
|
||||
$$renderer2.push(`<!----></main></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="min-h-dvh"><header class="flex h-14 items-center justify-between border-b border-border bg-surface-900/80 px-6 backdrop-blur-md"><a href="/" class="flex items-center gap-2.5"><div class="font-800 flex h-8 w-8 items-center justify-center rounded-md bg-accent text-sm text-surface-900">V</div> <span class="font-700 text-base tracking-tight text-text-primary">Vash Esports</span></a> <a href="/login" class="font-600 rounded-md bg-accent px-3.5 py-1.5 text-sm text-surface-900 transition-colors hover:bg-accent-hover">Sign in with osu!</a></header> <main class="p-6">`);
|
||||
children($$renderer2);
|
||||
$$renderer2.push(`<!----></main></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_layout as default
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,118 +0,0 @@
|
||||
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(`<div class="flex min-h-[70vh] flex-col items-center justify-center text-center"><div class="font-800 mb-6 flex h-16 w-16 items-center justify-center rounded-xl bg-accent text-2xl text-surface-900">V</div> <h1 class="text-4xl font-800 tracking-tight">Welcome to <span class="text-accent">Vash Esports</span></h1> <p class="mt-3 max-w-md text-text-secondary">Automated tournament match management for osu! — lobbies, mappools, picks, bans, and
|
||||
ELO tracking all in one place.</p> <a href="/login" class="font-600 mt-8 rounded-lg bg-accent px-6 py-3 text-surface-900 transition-colors hover:bg-accent-hover">Sign in with osu!</a> <p class="mt-4 text-xs text-text-secondary">Uses your osu! account. No extra registration needed.</p></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="mx-auto max-w-5xl"><div><h1 class="text-2xl font-700 tracking-tight">Welcome back, <span class="text-accent">${escape_html(data.user.name)}</span></h1> <p class="mt-1 text-sm text-text-secondary">Automated esports match management for osu!</p></div> <div class="mt-6 grid grid-cols-2 gap-3 sm:grid-cols-4"><div class="rounded-lg border border-border bg-surface-800 p-4"><p class="text-2xl font-800 tabular-nums text-text-primary">${escape_html(data.stats.matches)}</p> <p class="mt-1 text-xs text-text-secondary">Total Matches</p></div> <div class="rounded-lg border border-border bg-surface-800 p-4"><p class="text-2xl font-800 tabular-nums text-green-400">${escape_html(data.stats.finished)}</p> <p class="mt-1 text-xs text-text-secondary">Completed</p></div> <div class="rounded-lg border border-border bg-surface-800 p-4"><p class="text-2xl font-800 tabular-nums text-text-primary">${escape_html(data.stats.teams)}</p> <p class="mt-1 text-xs text-text-secondary">Teams</p></div> <div class="rounded-lg border border-border bg-surface-800 p-4"><p class="text-2xl font-800 tabular-nums text-text-primary">${escape_html(data.stats.mappools)}</p> <p class="mt-1 text-xs text-text-secondary">Mappools</p></div></div> `);
|
||||
if (data.liveMatches.length > 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-8"><div class="flex items-center gap-2"><div class="relative h-2 w-2"><div class="absolute inset-0 animate-ping rounded-full bg-green-400 opacity-75"></div> <div class="relative h-2 w-2 rounded-full bg-green-400"></div></div> <h2 class="text-sm font-600">Live Now</h2></div> <div class="mt-3 flex flex-col gap-2"><!--[-->`);
|
||||
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(`<a${attr("href", `/matches/${stringify(m.id)}`)} class="flex items-center gap-4 rounded-lg border border-green-500/20 bg-surface-800 p-4 transition-all hover:border-green-500/40 hover:bg-surface-700"><div class="flex flex-1 items-center gap-3">`);
|
||||
if (p1?.team.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", p1.team.avatarUrl)} alt="" class="h-8 w-8 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span class="text-sm font-600">${escape_html(p1?.team.name ?? "?")}</span></div> <div class="flex items-center gap-3"><span class="text-xl font-800 tabular-nums">${escape_html(p1?.score ?? 0)}</span> <span class="text-xs text-text-secondary">vs</span> <span class="text-xl font-800 tabular-nums">${escape_html(p2?.score ?? 0)}</span></div> <div class="flex flex-1 items-center justify-end gap-3"><span class="text-sm font-600">${escape_html(p2?.team.name ?? "?")}</span> `);
|
||||
if (p2?.team.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", p2.team.avatarUrl)} alt="" class="h-8 w-8 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <span${attr_class(`flex items-center gap-1.5 text-xs font-500 ${stringify(sc?.color ?? "")}`)}>`);
|
||||
if (sc?.dot) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span${attr_class(`h-1.5 w-1.5 rounded-full ${stringify(sc.dot)}`)}></span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> ${escape_html(sc?.label)}</span></a>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="mt-6 rounded-lg border border-border bg-surface-800 p-5"><div class="flex items-center justify-between"><div><h2 class="text-sm font-600">Ranked Queue</h2> <p class="mt-0.5 text-xs text-text-secondary">Find a match at your skill level</p></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<button${attr("disabled", queueLoading, true)} class="rounded-md bg-accent px-4 py-2 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover disabled:opacity-50">${escape_html("Find Match")}</button>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div> <div class="mt-8 grid gap-4 lg:grid-cols-3"><div class="lg:col-span-1"><h2 class="text-sm font-600">Quick Actions</h2> <div class="mt-3 flex flex-col gap-2"><a href="/matches" class="flex items-center gap-3 rounded-lg border border-border bg-surface-800 p-3 transition-colors hover:border-accent/40 hover:bg-surface-700"><span class="flex h-8 w-8 items-center justify-center rounded-md bg-accent/10 text-accent">⚔</span> <div><p class="text-sm font-600">New Match</p> <p class="text-xs text-text-secondary">Start a 1v1</p></div></a> <a href="/mappools" class="flex items-center gap-3 rounded-lg border border-border bg-surface-800 p-3 transition-colors hover:border-accent/40 hover:bg-surface-700"><span class="flex h-8 w-8 items-center justify-center rounded-md bg-purple-500/10 text-purple-400">♫</span> <div><p class="text-sm font-600">Mappools</p> <p class="text-xs text-text-secondary">Build a pool</p></div></a> <a href="/teams" class="flex items-center gap-3 rounded-lg border border-border bg-surface-800 p-3 transition-colors hover:border-accent/40 hover:bg-surface-700"><span class="flex h-8 w-8 items-center justify-center rounded-md bg-blue-500/10 text-blue-400">⚑</span> <div><p class="text-sm font-600">Teams</p> <p class="text-xs text-text-secondary">Manage rosters</p></div></a></div></div> <div class="lg:col-span-2"><h2 class="text-sm font-600">Recent Matches</h2> <div class="mt-3 flex flex-col gap-2">`);
|
||||
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(`<a${attr("href", `/matches/${stringify(m.id)}`)} class="flex items-center gap-3 rounded-lg border border-border bg-surface-800 p-3 transition-colors hover:border-accent/30 hover:bg-surface-700"><div class="min-w-0 flex-1"><div class="flex items-center gap-2"><span class="text-sm font-600">${escape_html(p1?.team.name ?? "?")}</span> `);
|
||||
if (m.state === "FINISHED") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span${attr_class(`text-xs font-700 tabular-nums ${stringify((p1?.score ?? 0) > (p2?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p1?.score ?? 0)}</span> <span class="text-xs text-text-secondary">-</span> <span${attr_class(`text-xs font-700 tabular-nums ${stringify((p2?.score ?? 0) > (p1?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p2?.score ?? 0)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<span class="text-xs text-text-secondary">vs</span>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span class="text-sm font-600">${escape_html(p2?.team.name ?? "?")}</span></div> <p class="mt-0.5 text-xs text-text-secondary">${escape_html(m.name ? `${m.name} · ` : "")}BO${escape_html(config.bestOf)}
|
||||
· ${escape_html(timeAgo(m.finishedAt ?? m.createdAt))}</p></div> `);
|
||||
if (m.state === "FINISHED" && m.winnerId) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
const winner = m.participants.find((p) => p.teamId === m.winnerId);
|
||||
$$renderer2.push(`<span class="text-xs text-green-400">🏆 ${escape_html(winner?.team.name)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span${attr_class(`text-xs ${stringify(sc?.color ?? "text-text-secondary")}`)}>${escape_html(sc?.label)}</span></a>`);
|
||||
}
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="rounded-lg border border-dashed border-border py-8 text-center"><p class="text-sm text-text-secondary">No matches yet. <a href="/matches" class="text-accent hover:underline">Create one</a></p></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div></div></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,70 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,70 +0,0 @@
|
||||
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(`<div class="mx-auto max-w-4xl"><div><h1 class="text-2xl font-700 tracking-tight">Admin Panel</h1> <p class="mt-1 text-sm text-text-secondary">${escape_html(data.users.length)} registered user${escape_html(data.users.length !== 1 ? "s" : "")}</p></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="mt-6 rounded-lg border border-border bg-surface-800"><div class="grid grid-cols-[auto_1fr_auto_auto_auto] gap-4 border-b border-border px-4 py-3"><span class="text-xs font-600 text-text-secondary">Avatar</span> <span class="text-xs font-600 text-text-secondary">User</span> <span class="text-xs font-600 text-text-secondary">ELO</span> <span class="text-xs font-600 text-text-secondary">W/L</span> <span class="text-xs font-600 text-text-secondary">Role</span></div> <!--[-->`);
|
||||
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(`<div class="grid grid-cols-[auto_1fr_auto_auto_auto] items-center gap-4 border-b border-border/50 px-4 py-3 last:border-b-0">`);
|
||||
if (u.image) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", u.image)} alt="" class="h-8 w-8 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="flex h-8 w-8 items-center justify-center rounded-full bg-surface-600 text-xs font-700 text-text-secondary">${escape_html(u.name.charAt(0).toUpperCase())}</div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="min-w-0"><p class="truncate text-sm font-500">${escape_html(u.name)}</p> <p class="truncate text-xs text-text-secondary">${escape_html(u.email)}</p></div> <span class="text-sm font-600 tabular-nums text-text-primary">${escape_html(u.elo)}</span> <span class="text-xs tabular-nums text-text-secondary"><span class="text-green-400">${escape_html(u.wins)}W</span> / <span class="text-red-400">${escape_html(u.losses)}L</span></span> <form method="post" action="?/setRole" class="flex items-center gap-2"><input type="hidden" name="userId"${attr("value", u.id)}/> `);
|
||||
$$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(`</form></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,100 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,98 +0,0 @@
|
||||
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(`<div class="mx-auto max-w-4xl"><div class="flex items-center justify-between"><div class="flex items-center gap-3"><a href="/mappools" class="text-text-secondary transition-colors hover:text-text-primary">←</a> <div><h1 class="text-2xl font-700 tracking-tight">${escape_html(data.pool.name)}</h1> <p class="mt-1 text-sm text-text-secondary">${escape_html(data.pool.slots.length)} map${escape_html(data.pool.slots.length !== 1 ? "s" : "")}</p></div></div> <form method="post" action="?/deletePool"><button type="submit" class="rounded-md border border-red-500/30 px-3 py-1.5 text-sm text-red-400 transition-colors hover:bg-red-500/10">Delete Pool</button></form></div> <form method="post" action="?/addSlot" class="mt-6 rounded-lg border border-border bg-surface-800 p-4"><h2 class="text-sm font-600">Add Map</h2> <div class="mt-3 flex gap-3">`);
|
||||
$$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(` <input type="text" name="beatmapId"${attr("value", beatmapInput)} placeholder="Beatmap ID or URL (e.g. 75 or https://osu.ppy.sh/beatmaps/75)" class="flex-1 rounded-md border border-border bg-surface-700 px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary/50 focus:border-accent focus:outline-none"/> <button type="submit"${attr("disabled", !beatmapInput.trim(), true)} class="rounded-md bg-accent px-4 py-2 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover disabled:opacity-50">${escape_html("Add")}</button></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></form> <div class="mt-6 flex flex-col gap-6">`);
|
||||
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(`<div><div class="mb-2 flex items-center gap-2"><span${attr_class(`rounded border px-2 py-0.5 text-xs font-600 ${stringify(categoryColors[category] ?? "bg-surface-600 text-text-secondary border-border")}`)}>${escape_html(category)}</span> <span class="text-xs text-text-secondary">${escape_html(slots.length)} map${escape_html(slots.length !== 1 ? "s" : "")}</span></div> <div class="flex flex-col gap-2"><!--[-->`);
|
||||
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(`<div class="group flex items-center gap-3 rounded-lg border border-border bg-surface-800 p-2 transition-colors hover:border-accent/30">`);
|
||||
if (slot.beatmap?.coverUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", slot.beatmap.coverUrl)} alt="" class="h-12 w-24 rounded object-cover"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="flex h-12 w-24 items-center justify-center rounded bg-surface-700 text-xs text-text-secondary">No cover</div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="min-w-0 flex-1">`);
|
||||
if (slot.beatmap) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<a${attr("href", slot.beatmap.url)} target="_blank" rel="noopener" class="truncate text-sm font-500 hover:text-accent">${escape_html(slot.beatmap.artist)} - ${escape_html(slot.beatmap.title)}</a> <div class="mt-0.5 flex items-center gap-3 text-xs text-text-secondary"><span>[${escape_html(slot.beatmap.version)}]</span> <span>★ ${escape_html(slot.beatmap.starRating.toFixed(2))}</span> <span>${escape_html(slot.beatmap.bpm)} BPM</span> <span>${escape_html(formatLength(slot.beatmap.totalLength))}</span></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<p class="text-sm text-text-secondary">Beatmap #${escape_html(slot.beatmapId)}</p>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <span class="text-xs font-600 text-text-secondary">${escape_html(category)}${escape_html(i + 1)}</span> <form method="post" action="?/removeSlot"><input type="hidden" name="slotId"${attr("value", slot.id)}/> <button type="submit" class="rounded p-1 text-text-secondary opacity-0 transition-all hover:bg-red-500/10 hover:text-red-400 group-hover:opacity-100">✕</button></form></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
}
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="rounded-lg border border-dashed border-border py-12 text-center"><p class="text-sm text-text-secondary">No maps yet. Add your first map above.</p></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
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(`<div class="mx-auto max-w-4xl"><div class="flex items-center justify-between"><div><h1 class="text-2xl font-700 tracking-tight">Mappools</h1> <p class="mt-1 text-sm text-text-secondary">Create and manage your beatmap pools</p></div> <button class="rounded-md bg-accent px-4 py-2 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover">${escape_html("New Pool")}</button></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="mt-6 flex flex-col gap-3">`);
|
||||
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(`<a${attr("href", `/mappools/${stringify(pool.id)}`)} class="group flex items-center justify-between rounded-lg border border-border bg-surface-800 p-4 transition-colors hover:border-accent/40 hover:bg-surface-700"><div><h2 class="text-sm font-600">${escape_html(pool.name)}</h2> <p class="mt-1 text-xs text-text-secondary">${escape_html(pool.slots.length)} map${escape_html(pool.slots.length !== 1 ? "s" : "")}
|
||||
· Created ${escape_html(new Date(pool.createdAt).toLocaleDateString())}</p></div> <span class="text-text-secondary transition-colors group-hover:text-accent">→</span></a>`);
|
||||
}
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="rounded-lg border border-dashed border-border py-12 text-center"><p class="text-sm text-text-secondary">No mappools yet. Create one to get started.</p></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,129 +0,0 @@
|
||||
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 <slot> (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
|
||||
};
|
||||
@@ -1,323 +0,0 @@
|
||||
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(`<div class="mx-auto max-w-5xl"><div class="flex items-center gap-3"><a href="/matches" class="text-text-secondary transition-colors hover:text-text-primary">←</a> <div class="flex-1"><h1 class="text-xl font-700 tracking-tight">${escape_html(m().name || "Match")}</h1> <p class="mt-0.5 text-xs text-text-secondary">Best of ${escape_html(config().bestOf)} · First to ${escape_html(winsNeeded())} `);
|
||||
if (m().osuLobbyId) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`· <a${attr("href", `https://osu.ppy.sh/mp/${stringify(m().osuLobbyId)}`)} target="_blank" class="text-accent hover:underline">osu! mp/${escape_html(m().osuLobbyId)}</a>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></p></div> <span${attr_class(`rounded border px-2.5 py-1 text-xs font-600 ${stringify(stateBorder(m().state))}`)}>${escape_html(stateLabel(m().state))}</span> `);
|
||||
if (!["FINISHED", "CANCELLED"].includes(m().state)) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<form method="post" action="?/cancel"><button type="submit" class="rounded-md border border-red-500/30 px-3 py-1.5 text-xs text-red-400 hover:bg-red-500/10">Cancel</button></form>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> `);
|
||||
if (p1() && p2()) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6 flex items-center gap-4"><div${attr_class(`flex flex-1 items-center gap-4 rounded-lg border p-4 ${stringify(m().winnerId === p1().teamId ? "border-green-500/40 bg-green-500/5" : "border-border bg-surface-800")}`)}>`);
|
||||
if (p1().team.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", p1().team.avatarUrl)} alt="" class="h-10 w-10 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="flex-1"><p class="text-sm font-600">${escape_html(p1().team.name)}</p> `);
|
||||
if (p1().rollValue != null) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<p class="text-xs text-text-secondary">Roll: ${escape_html(p1().rollValue)}${escape_html(p1().pickOrder === 1 ? " ★" : "")}</p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <span${attr_class(`text-3xl font-800 tabular-nums ${stringify(p1().score >= winsNeeded() ? "text-green-400" : "text-text-primary")}`)}>${escape_html(p1().score)}</span></div> <span class="text-lg font-700 text-text-secondary">vs</span> <div${attr_class(`flex flex-1 items-center gap-4 rounded-lg border p-4 ${stringify(m().winnerId === p2().teamId ? "border-green-500/40 bg-green-500/5" : "border-border bg-surface-800")}`)}><span${attr_class(`text-3xl font-800 tabular-nums ${stringify(p2().score >= winsNeeded() ? "text-green-400" : "text-text-primary")}`)}>${escape_html(p2().score)}</span> <div class="flex-1 text-right"><p class="text-sm font-600">${escape_html(p2().team.name)}</p> `);
|
||||
if (p2().rollValue != null) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<p class="text-xs text-text-secondary">${escape_html(p2().pickOrder === 1 ? "★ " : "")}Roll: ${escape_html(p2().rollValue)}</p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> `);
|
||||
if (p2().team.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", p2().team.avatarUrl)} alt="" class="h-10 w-10 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (["LOBBY", "ROLLING", "PICKING", "PLAYING"].includes(m().state)) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-3 flex justify-center"><form method="post" action="?/reinvite"><button type="submit" class="rounded-md border border-border px-3 py-1.5 text-xs text-text-secondary transition-colors hover:border-accent/40 hover:text-accent">📨 Re-invite players to lobby</button></form></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (m().state === "ROLLING") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"><div class="w-full max-w-md rounded-xl border border-yellow-500/30 bg-surface-800 p-8 text-center shadow-2xl"><h2 class="text-lg font-700">🎲 Roll Phase</h2> <p class="mt-2 text-sm text-text-secondary">Highest roll picks first</p> <div class="mt-6 flex justify-center gap-6"><!--[-->`);
|
||||
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(`<div class="rounded-lg border border-border bg-surface-700 px-6 py-4"><p class="text-xs text-text-secondary">${escape_html(p.team.name)}</p> `);
|
||||
if (p.rollValue != null) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<p class="mt-1 text-2xl font-800 text-yellow-400">${escape_html(p.rollValue)}</p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<p class="mt-1 text-2xl font-800 text-text-secondary/30">—</p>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> `);
|
||||
if (canRoll()) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<form method="post" action="?/roll"><button type="submit"${attr("disabled", rolling, true)} class="mt-6 rounded-md bg-accent px-8 py-3 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover disabled:opacity-50">${escape_html("🎲 Roll!")}</button></form> <p class="mt-3 text-xs text-text-secondary">or type <code class="rounded bg-surface-700 px-1.5 py-0.5 font-mono text-accent">!roll</code> in osu! chat</p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<p class="mt-6 text-sm text-text-secondary animate-pulse">Waiting for all rolls...</p>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (m().state === "PICKING") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"><div class="w-full max-w-3xl max-h-[80vh] overflow-y-auto rounded-xl border border-blue-500/30 bg-surface-800 p-6 shadow-2xl"><div class="text-center"><h2 class="text-lg font-700">🎯 Pick a Map</h2> <p class="mt-1 text-sm"><span class="font-600 text-accent">${escape_html(expectedPicker()?.team.name)}</span>'s turn to pick</p> <p class="mt-1 text-xs text-text-secondary">or type <code class="rounded bg-surface-700 px-1.5 py-0.5 font-mono text-accent">!pick NM1</code> in osu! chat</p></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="mt-6 flex flex-col gap-4"><!--[-->`);
|
||||
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(`<div><span${attr_class(`mb-2 inline-block rounded border px-2 py-0.5 text-xs font-600 ${stringify(catColors[category] ?? "border-border")}`)}>${escape_html(category)}</span> <div class="grid grid-cols-1 gap-2 sm:grid-cols-2"><!--[-->`);
|
||||
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(`<form method="post" action="?/pick"><input type="hidden" name="slotId"${attr("value", slot.id)}/> <button type="submit"${attr("disabled", isPlayed || picking, true)}${attr_class(`flex w-full items-center gap-3 rounded-lg border p-2 text-left transition-all ${stringify(isPlayed ? "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")}`)}>`);
|
||||
if (bm?.listCoverUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", bm.listCoverUrl)} alt="" class="h-10 w-20 rounded object-cover"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="flex h-10 w-20 items-center justify-center rounded bg-surface-600 text-xs text-text-secondary">?</div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="min-w-0 flex-1">`);
|
||||
if (bm) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<p class="truncate text-xs font-500">${escape_html(bm.artist)} - ${escape_html(bm.title)}</p> <p class="text-xs text-text-secondary">[${escape_html(bm.version)}] · ★${escape_html(bm.starRating.toFixed(1))} · ${escape_html(bm.bpm)}bpm</p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<p class="text-xs text-text-secondary">#${escape_html(slot.beatmapId)}</p>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <span class="text-xs font-600 text-text-secondary">${escape_html(category)}${escape_html(slot.orderInCategory)}</span> `);
|
||||
if (isPlayed) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span class="text-xs text-text-secondary">✓</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></button></form>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div></div>`);
|
||||
} 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(`<div class="mt-6 rounded-lg border border-green-500/30 bg-green-500/5 p-5 text-center"><div class="flex items-center justify-center gap-2"><div class="relative h-3 w-3"><div class="absolute inset-0 animate-ping rounded-full bg-green-400 opacity-75"></div> <div class="relative h-3 w-3 rounded-full bg-green-400"></div></div> <span class="text-sm font-600 text-green-400">Now Playing</span></div> `);
|
||||
if (bm) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<p class="mt-2 text-sm">${escape_html(bm.artist)} - ${escape_html(bm.title)} <span class="text-text-secondary">[${escape_html(bm.version)}]</span></p> <p class="mt-1 text-xs text-text-secondary">★${escape_html(bm.starRating.toFixed(1))} · ${escape_html(bm.bpm)}bpm · ${escape_html(formatLength(bm.totalLength))}</p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <p class="mt-3 text-xs text-text-secondary animate-pulse">Waiting for players to ready up & play in osu!...</p> <form method="post" action="?/forceStart"><button type="submit"${attr("disabled", forceStarting, true)} class="mt-4 rounded-md border border-yellow-500/30 px-4 py-2 text-xs font-600 text-yellow-400 transition-colors hover:bg-yellow-500/10 disabled:opacity-50">${escape_html("Force Start (skip ready)")}</button></form></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (m().state === "FINISHED") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
const winner = m().participants.find((p) => p.teamId === m().winnerId);
|
||||
$$renderer2.push(`<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"><div class="w-full max-w-md rounded-xl border border-accent/30 bg-surface-800 p-8 text-center shadow-2xl"><div class="text-4xl">🏆</div> <h2 class="mt-4 text-2xl font-800"><span class="text-accent">${escape_html(winner?.team.name)}</span> wins!</h2> <p class="mt-2 text-lg font-700 tabular-nums text-text-secondary">${escape_html(p1()?.score)} – ${escape_html(p2()?.score)}</p> <div class="mt-6 flex flex-col gap-1"><!--[-->`);
|
||||
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(`<div class="flex items-center gap-2 rounded bg-surface-700 px-3 py-1.5 text-xs"><span${attr_class(`font-600 ${stringify(catColors[game.slot?.category]?.split(" ")[1] ?? "text-text-secondary")}`)}>${escape_html(game.slot?.category)}${escape_html(game.slot?.orderInCategory)}</span> <span class="flex-1 truncate text-text-secondary">${escape_html(bm ? `${bm.artist} - ${bm.title}` : `#${game.slot?.beatmapId}`)}</span> <span${attr_class(`font-600 ${stringify(winnerP?.id === p1()?.id ? "text-blue-400" : "text-red-400")}`)}>${escape_html(winnerP?.team.name)}</span></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <a href="/" class="mt-6 inline-block rounded-md bg-accent px-6 py-2.5 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover">Back to Dashboard</a></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (m().games.length > 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6"><h2 class="text-sm font-600">Games Played</h2> <div class="mt-3 flex flex-col gap-2"><!--[-->`);
|
||||
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(`<div class="flex items-center gap-3 rounded-lg border border-border bg-surface-800 p-3"><span class="w-6 text-center text-xs font-600 text-text-secondary">${escape_html(game.gameNumber)}</span> `);
|
||||
if (bm?.listCoverUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", bm.listCoverUrl)} alt="" class="h-8 w-16 rounded object-cover"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="min-w-0 flex-1">`);
|
||||
if (bm) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<p class="truncate text-sm font-500">${escape_html(bm.artist)} - ${escape_html(bm.title)} <span class="text-text-secondary">[${escape_html(bm.version)}]</span></p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="flex items-center gap-2 text-xs text-text-secondary"><span${attr_class(`rounded border px-1.5 py-0.5 ${stringify(catColors[game.slot?.category] ?? "border-border")}`)}>${escape_html(game.slot?.category)}${escape_html(game.slot?.orderInCategory)}</span> `);
|
||||
if (pickerTeam) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span>Picked by ${escape_html(pickerTeam.name)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div> `);
|
||||
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(`<div class="flex items-center gap-2 font-mono text-sm tabular-nums"><span${attr_class(p1Score > p2Score ? "font-700 text-green-400" : "text-text-secondary")}>${escape_html(p1Score.toLocaleString())}</span> <span class="text-text-secondary">-</span> <span${attr_class(p2Score > p1Score ? "font-700 text-green-400" : "text-text-secondary")}>${escape_html(p2Score.toLocaleString())}</span></div>`);
|
||||
} else if (game.state === "PLAYING") {
|
||||
$$renderer2.push("<!--[1-->");
|
||||
$$renderer2.push(`<span class="text-xs text-green-400 animate-pulse">Live</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (winnerTeam) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span class="text-xs text-green-400">✓ ${escape_html(winnerTeam.name)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (m().state === "CREATED") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6 rounded-lg border border-border bg-surface-800 p-6 text-center"><p class="text-sm text-text-secondary animate-pulse">Creating osu! lobby...</p></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (m().state === "LOBBY") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6 rounded-lg border border-yellow-500/30 bg-yellow-500/5 p-6 text-center"><p class="text-sm text-text-secondary">Lobby created. Waiting for players to join...</p> `);
|
||||
if (m().osuLobbyId) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<p class="mt-2 text-xs text-text-secondary"><a${attr("href", `https://osu.ppy.sh/mp/${stringify(m().osuLobbyId)}`)} target="_blank" class="text-accent hover:underline">osu! mp/${escape_html(m().osuLobbyId)}</a></p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (m().state === "CANCELLED") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6 rounded-lg border border-red-500/30 bg-red-500/5 p-6 text-center"><p class="text-sm text-red-400">This match was cancelled.</p></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,164 +0,0 @@
|
||||
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(`<div class="mx-auto max-w-5xl"><div class="flex items-center justify-between"><div><h1 class="text-2xl font-700 tracking-tight">Matches</h1> <p class="mt-1 text-sm text-text-secondary">${escape_html(data.matches.length)} match${escape_html(data.matches.length !== 1 ? "es" : "")} `);
|
||||
if (liveMatches().length > 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`· <span class="text-green-400">${escape_html(liveMatches().length)} live</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></p></div> <button class="rounded-md bg-accent px-4 py-2 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover">${escape_html("New Match")}</button></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (liveMatches().length > 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6"><h2 class="flex items-center gap-2 text-sm font-600"><div class="relative h-2 w-2"><div class="absolute inset-0 animate-ping rounded-full bg-green-400 opacity-75"></div> <div class="relative h-2 w-2 rounded-full bg-green-400"></div></div> Live Now</h2> <div class="mt-3 flex flex-col gap-2"><!--[-->`);
|
||||
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(`<a${attr("href", `/matches/${stringify(m.id)}`)} class="group flex items-center gap-4 rounded-lg border border-green-500/20 bg-surface-800 p-4 transition-all hover:border-green-500/40 hover:bg-surface-700"><div class="flex flex-1 items-center gap-3">`);
|
||||
if (p1?.team.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", p1.team.avatarUrl)} alt="" class="h-8 w-8 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span class="text-sm font-600">${escape_html(p1?.team.name ?? "?")}</span></div> <div class="flex items-center gap-3"><span class="text-xl font-800 tabular-nums">${escape_html(p1?.score ?? 0)}</span> <span class="text-xs font-600 text-text-secondary">vs</span> <span class="text-xl font-800 tabular-nums">${escape_html(p2?.score ?? 0)}</span></div> <div class="flex flex-1 items-center justify-end gap-3"><span class="text-sm font-600">${escape_html(p2?.team.name ?? "?")}</span> `);
|
||||
if (p2?.team.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", p2.team.avatarUrl)} alt="" class="h-8 w-8 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <span${attr_class(`flex items-center gap-1.5 rounded border px-2 py-0.5 text-xs font-500 ${stringify(sc?.color ?? "border-border")}`)}>`);
|
||||
if (sc?.dot) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span${attr_class(`h-1.5 w-1.5 rounded-full ${stringify(sc.dot)}`)}></span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> ${escape_html(sc?.label ?? m.state)}</span></a>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="mt-6">`);
|
||||
if (liveMatches().length > 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<h2 class="text-sm font-600 text-text-secondary">Past Matches</h2>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="mt-3 flex flex-col gap-2">`);
|
||||
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(`<a${attr("href", `/matches/${stringify(m.id)}`)} class="group flex items-center gap-4 rounded-lg border border-border bg-surface-800 p-3 transition-colors hover:border-accent/30 hover:bg-surface-700"><div class="min-w-0 flex-1"><div class="flex items-center gap-2"><span class="text-sm font-600">${escape_html(p1?.team.name ?? "?")}</span> `);
|
||||
if (m.state === "FINISHED") {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span${attr_class(`text-xs font-700 tabular-nums ${stringify((p1?.score ?? 0) > (p2?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p1?.score ?? 0)}</span> <span class="text-xs text-text-secondary">-</span> <span${attr_class(`text-xs font-700 tabular-nums ${stringify((p2?.score ?? 0) > (p1?.score ?? 0) ? "text-green-400" : "text-text-secondary")}`)}>${escape_html(p2?.score ?? 0)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<span class="text-xs text-text-secondary">vs</span>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span class="text-sm font-600">${escape_html(p2?.team.name ?? "?")}</span></div> <p class="mt-0.5 text-xs text-text-secondary">${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("<!--[1-->");
|
||||
$$renderer2.push(`· ${escape_html(timeAgo(m.createdAt))}`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></p></div> `);
|
||||
if (m.state === "FINISHED" && m.winnerId) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
const winner = m.participants.find((p) => p.teamId === m.winnerId);
|
||||
$$renderer2.push(`<span class="text-xs font-500 text-green-400">🏆 ${escape_html(winner?.team.name)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span${attr_class(`rounded border px-2 py-0.5 text-xs font-500 ${stringify(sc?.color ?? "border-border")}`)}>${escape_html(sc?.label ?? m.state)}</span></a>`);
|
||||
}
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
if (liveMatches().length === 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="rounded-lg border border-dashed border-border py-12 text-center"><p class="text-sm text-text-secondary">No matches yet. Create one to get started.</p></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
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(`<div class="mx-auto max-w-2xl"><h1 class="text-2xl font-700 tracking-tight">Settings</h1> <p class="mt-1 text-sm text-text-secondary">Your profile and account settings</p> <div class="mt-6 rounded-lg border border-border bg-surface-800 p-6"><div class="flex items-center gap-4">`);
|
||||
if (data.profile.image) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", data.profile.image)}${attr("alt", data.profile.name)} class="h-16 w-16 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="flex h-16 w-16 items-center justify-center rounded-full bg-surface-600 text-xl font-700 text-text-secondary">${escape_html(data.profile.name.charAt(0).toUpperCase())}</div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="flex-1"><div class="flex items-center gap-2"><h2 class="text-lg font-700">${escape_html(data.profile.name)}</h2> <span${attr_class(`rounded px-2 py-0.5 text-xs font-600 ${stringify(badge().color)}`)}>${escape_html(badge().label)}</span></div> <p class="mt-0.5 text-sm text-text-secondary">${escape_html(data.profile.email)}</p></div></div> <div class="mt-6 border-t border-border pt-4"><h3 class="text-xs font-600 text-text-secondary uppercase tracking-wider">Connected Accounts</h3> <div class="mt-3 flex items-center gap-3 rounded-md bg-surface-700 px-4 py-3"><span class="text-lg">🎮</span> <div class="flex-1"><p class="text-sm font-500">osu!</p> <p class="text-xs text-text-secondary">`);
|
||||
if (data.hasOsuLinked) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`Connected as ${escape_html(data.profile.name)}`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`Not connected`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></p></div> `);
|
||||
if (data.hasOsuLinked) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span class="rounded bg-green-500/20 px-2 py-0.5 text-xs font-500 text-green-400">Linked</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<a href="/api/auth/osu/login" class="rounded bg-accent px-3 py-1 text-xs font-600 text-surface-900 hover:bg-accent-hover">Connect</a>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div></div> <div class="mt-4 rounded-lg border border-border bg-surface-800 p-6"><h3 class="text-xs font-600 text-text-secondary uppercase tracking-wider">Ranked Stats</h3> <div class="mt-4 grid grid-cols-4 gap-4"><div><p class="text-2xl font-800 tabular-nums text-accent">${escape_html(data.rating.elo)}</p> <p class="mt-1 text-xs text-text-secondary">ELO Rating</p></div> <div><p class="text-2xl font-800 tabular-nums text-green-400">${escape_html(data.rating.wins)}</p> <p class="mt-1 text-xs text-text-secondary">Wins</p></div> <div><p class="text-2xl font-800 tabular-nums text-red-400">${escape_html(data.rating.losses)}</p> <p class="mt-1 text-xs text-text-secondary">Losses</p></div> <div><p class="text-2xl font-800 tabular-nums text-text-primary">${escape_html(winRate())}${escape_html(winRate() !== "—" ? "%" : "")}</p> <p class="mt-1 text-xs text-text-secondary">Win Rate</p></div></div></div> <div class="mt-4 rounded-lg border border-border bg-surface-800 p-6"><h3 class="text-xs font-600 text-red-400 uppercase tracking-wider">Account</h3> <div class="mt-4"><form method="post" action="?/logout"><button type="submit" class="rounded-md border border-red-500/30 px-4 py-2 text-sm font-500 text-red-400 transition-colors hover:bg-red-500/10">Sign Out</button></form></div></div></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,88 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,138 +0,0 @@
|
||||
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(`<div class="mx-auto max-w-4xl"><div class="flex items-center justify-between"><div><h1 class="text-2xl font-700 tracking-tight">Teams</h1> <p class="mt-1 text-sm text-text-secondary">${escape_html(data.teams.length)} team${escape_html(data.teams.length !== 1 ? "s" : "")}</p></div> <button class="rounded-md bg-accent px-4 py-2 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover">${escape_html("New Team")}</button></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
{
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (myTeams().length > 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6"><h2 class="text-sm font-600 text-text-secondary">My Teams</h2> <div class="mt-3 flex flex-col gap-3"><!--[-->`);
|
||||
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(`<div class="rounded-lg border border-border bg-surface-800 transition-colors"><button class="flex w-full items-center gap-4 p-4 text-left transition-colors hover:bg-surface-700">`);
|
||||
if (t.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", t.avatarUrl)} alt="" class="h-10 w-10 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="flex h-10 w-10 items-center justify-center rounded-full bg-surface-600 text-sm font-700 text-text-secondary">${escape_html(t.name.charAt(0).toUpperCase())}</div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="flex-1"><div class="flex items-center gap-2"><span class="text-sm font-600">${escape_html(t.name)}</span> `);
|
||||
if (t.isPersonal) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<span class="rounded bg-surface-600 px-1.5 py-0.5 text-[10px] font-500 text-text-secondary">solo</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <p class="mt-0.5 text-xs text-text-secondary">${escape_html(t.members.length)} member${escape_html(t.members.length !== 1 ? "s" : "")}</p></div> <span${attr_class(`text-sm text-text-secondary transition-transform ${stringify(expandedTeam === t.id ? "rotate-90" : "")}`)}>›</span></button> `);
|
||||
if (expandedTeam === t.id) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="border-t border-border px-4 pb-4 pt-3"><div class="flex flex-col gap-2"><!--[-->`);
|
||||
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(`<div class="flex items-center gap-3 rounded-md bg-surface-700 px-3 py-2">`);
|
||||
if (member.user?.image) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", member.user.image)} alt="" class="h-6 w-6 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="h-6 w-6 rounded-full bg-surface-600"></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <span class="flex-1 text-sm">${escape_html(member.user?.name ?? "Unknown")}</span> <span class="text-xs text-text-secondary">${escape_html(member.role)}</span> `);
|
||||
if (isOwner(t) && member.userId !== data.userId) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<form method="post" action="?/removeMember"><input type="hidden" name="memberId"${attr("value", member.id)}/> <button type="submit" class="text-xs text-red-400 opacity-50 transition-opacity hover:opacity-100">×</button></form>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> `);
|
||||
if (isOwner(t) && !t.isPersonal) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
if (addingMemberTo === t.id) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<form method="post" action="?/addMember" class="mt-3 flex gap-2"><input type="hidden" name="teamId"${attr("value", t.id)}/> <input type="text" name="username"${attr("value", memberUsername)} placeholder="osu! username" required="" class="flex-1 rounded-md border border-border bg-surface-600 px-3 py-1.5 text-sm text-text-primary placeholder:text-text-secondary/50 focus:border-accent focus:outline-none"/> <button type="submit" class="rounded-md bg-accent px-3 py-1.5 text-xs font-600 text-surface-900 hover:bg-accent-hover">Add</button> <button type="button" class="rounded-md border border-border px-3 py-1.5 text-xs text-text-secondary hover:bg-surface-600">Cancel</button></form>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<button class="mt-3 w-full rounded-md border border-dashed border-border py-2 text-xs text-text-secondary transition-colors hover:border-accent/40 hover:text-accent">+ Add Member</button>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (isOwner(t) && !t.isPersonal) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<form method="post" action="?/deleteTeam"><input type="hidden" name="teamId"${attr("value", t.id)}/> <button type="submit" class="mt-3 text-xs text-red-400 opacity-50 transition-opacity hover:opacity-100">Delete team</button></form>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (otherTeams().length > 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6"><h2 class="text-sm font-600 text-text-secondary">Other Teams</h2> <div class="mt-3 flex flex-col gap-2"><!--[-->`);
|
||||
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(`<div class="flex items-center gap-4 rounded-lg border border-border bg-surface-800 p-3">`);
|
||||
if (t.avatarUrl) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<img${attr("src", t.avatarUrl)} alt="" class="h-8 w-8 rounded-full"/>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`<div class="flex h-8 w-8 items-center justify-center rounded-full bg-surface-600 text-xs font-700 text-text-secondary">${escape_html(t.name.charAt(0).toUpperCase())}</div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="flex-1"><span class="text-sm font-600">${escape_html(t.name)}</span> <p class="text-xs text-text-secondary">${escape_html(t.members.length)} member${escape_html(t.members.length !== 1 ? "s" : "")}</p></div></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
if (data.teams.length === 0) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`<div class="mt-6 rounded-lg border border-dashed border-border py-12 text-center"><p class="text-sm text-text-secondary">No teams yet. Your personal team is created when you sign in.</p></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
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(`<div class="flex min-h-dvh items-center justify-center bg-surface-900"><div class="text-center"><p class="text-6xl font-800 text-accent">${escape_html(page.status)}</p> <h1 class="mt-4 text-xl font-700 text-text-primary">`);
|
||||
if (page.status === 404) {
|
||||
$$renderer2.push("<!--[-->");
|
||||
$$renderer2.push(`Page not found`);
|
||||
} else if (page.status === 403) {
|
||||
$$renderer2.push("<!--[1-->");
|
||||
$$renderer2.push(`Access denied`);
|
||||
} else if (page.status === 401) {
|
||||
$$renderer2.push("<!--[2-->");
|
||||
$$renderer2.push(`Not logged in`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[!-->");
|
||||
$$renderer2.push(`Something went wrong`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></h1> <p class="mt-2 text-sm text-text-secondary">${escape_html(page.error?.message ?? "An unexpected error occurred.")}</p> <a href="/" class="mt-6 inline-block rounded-md bg-accent px-5 py-2.5 text-sm font-600 text-surface-900 transition-colors hover:bg-accent-hover">Back to Dashboard</a></div></div>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_error as default
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
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<string, string>} */
|
||||
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(`<a${attr("href", resolve("/demo/better-auth"))}>better-auth</a>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
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(`<h1>Hi, ${escape_html(data.user.name)}!</h1> <p>Your user ID is ${escape_html(data.user.id)}.</p> <form method="post" action="?/signOut"><button class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700">Sign out</button></form>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
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(`<h1>Login</h1> <form method="post" action="?/signInEmail"><label>Email <input type="email" name="email" class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"/></label> <label>Password <input type="password" name="password" class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"/></label> <label>Name (for registration) <input name="name" class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"/></label> <button class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700">Login</button> <button formaction="?/signUpEmail" class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700">Register</button></form> <p class="text-red-500">${escape_html(form?.message ?? "")}</p> <hr class="my-4"/> <form method="post" action="?/signInSocial"><input type="hidden" name="provider" value="osu"/> <input type="hidden" name="callbackURL" value="/demo/better-auth"/> <button class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700">Sign in with osu!</button></form>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
import "./chunks/root.js";
|
||||
import { s, a } from "./chunks/environment.js";
|
||||
import { s as s2 } from "./chunks/server.js";
|
||||
import { g, o, a as a2, s as s3 } from "./chunks/internal.js";
|
||||
import { s as s4, a as a3 } from "./chunks/shared-server.js";
|
||||
export {
|
||||
g as get_hooks,
|
||||
o as options,
|
||||
s2 as set_assets,
|
||||
s as set_building,
|
||||
a2 as set_manifest,
|
||||
a as set_prerendering,
|
||||
s4 as set_private_env,
|
||||
a3 as set_public_env,
|
||||
s3 as set_read_implementation
|
||||
};
|
||||
@@ -1,226 +0,0 @@
|
||||
export const manifest = (() => {
|
||||
function __memo(fn) {
|
||||
let value;
|
||||
return () => value ??= (value = fn());
|
||||
}
|
||||
|
||||
return {
|
||||
appDir: "_app",
|
||||
appPath: "_app",
|
||||
assets: new Set(["android-chrome-192x192.png","android-chrome-512x512.png","apple-touch-icon.png","favicon-16x16.png","favicon-32x32.png","favicon.ico","robots.txt","site.webmanifest"]),
|
||||
mimeTypes: {".png":"image/png",".txt":"text/plain",".webmanifest":"application/manifest+json"},
|
||||
_: {
|
||||
client: {start:"_app/immutable/entry/start.Df1GVxyt.js",app:"_app/immutable/entry/app.DL20j9gb.js",imports:["_app/immutable/entry/start.Df1GVxyt.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/entry/app.DL20j9gb.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
||||
nodes: [
|
||||
__memo(() => import('./nodes/0.js')),
|
||||
__memo(() => import('./nodes/1.js')),
|
||||
__memo(() => import('./nodes/2.js')),
|
||||
__memo(() => import('./nodes/3.js')),
|
||||
__memo(() => import('./nodes/4.js')),
|
||||
__memo(() => import('./nodes/5.js')),
|
||||
__memo(() => import('./nodes/6.js')),
|
||||
__memo(() => import('./nodes/7.js')),
|
||||
__memo(() => import('./nodes/8.js')),
|
||||
__memo(() => import('./nodes/9.js')),
|
||||
__memo(() => import('./nodes/10.js')),
|
||||
__memo(() => import('./nodes/11.js')),
|
||||
__memo(() => import('./nodes/12.js')),
|
||||
__memo(() => import('./nodes/13.js')),
|
||||
__memo(() => import('./nodes/14.js'))
|
||||
],
|
||||
remotes: {
|
||||
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
id: "/(app)",
|
||||
pattern: /^\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 3 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/admin",
|
||||
pattern: /^\/admin\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 4 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/api/health",
|
||||
pattern: /^\/api\/health\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/health/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools",
|
||||
pattern: /^\/api\/mappools\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools/[id]",
|
||||
pattern: /^\/api\/mappools\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_id_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools/[id]/slots",
|
||||
pattern: /^\/api\/mappools\/([^/]+?)\/slots\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_id_/slots/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools/[id]/slots/[slotId]",
|
||||
pattern: /^\/api\/mappools\/([^/]+?)\/slots\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false},{"name":"slotId","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches",
|
||||
pattern: /^\/api\/matches\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/lobby",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/lobby\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/lobby/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/pick",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/pick\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/pick/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/rolling",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/rolling\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/rolling/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/roll",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/roll\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/roll/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/score",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/score\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/score/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/osu/beatmap/[id]",
|
||||
pattern: /^\/api\/osu\/beatmap\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/osu/beatmap/_id_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/queue",
|
||||
pattern: /^\/api\/queue\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/queue/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/demo",
|
||||
pattern: /^\/demo\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 11 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/demo/better-auth",
|
||||
pattern: /^\/demo\/better-auth\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 12 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/demo/better-auth/login",
|
||||
pattern: /^\/demo\/better-auth\/login\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 13 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/login",
|
||||
pattern: /^\/login\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 14 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/mappools",
|
||||
pattern: /^\/mappools\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 5 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/mappools/[id]",
|
||||
pattern: /^\/mappools\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 6 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/matches",
|
||||
pattern: /^\/matches\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 7 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/matches/[id]",
|
||||
pattern: /^\/matches\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 8 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/settings",
|
||||
pattern: /^\/settings\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 9 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/teams",
|
||||
pattern: /^\/teams\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 10 },
|
||||
endpoint: null
|
||||
}
|
||||
],
|
||||
prerendered_routes: new Set([]),
|
||||
matchers: async () => {
|
||||
|
||||
return { };
|
||||
},
|
||||
server_assets: {}
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -1,230 +0,0 @@
|
||||
export const manifest = (() => {
|
||||
function __memo(fn) {
|
||||
let value;
|
||||
return () => value ??= (value = fn());
|
||||
}
|
||||
|
||||
return {
|
||||
appDir: "_app",
|
||||
appPath: "_app",
|
||||
assets: new Set(["android-chrome-192x192.png","android-chrome-512x512.png","apple-touch-icon.png","favicon-16x16.png","favicon-32x32.png","favicon.ico","robots.txt","site.webmanifest"]),
|
||||
mimeTypes: {".png":"image/png",".txt":"text/plain",".webmanifest":"application/manifest+json"},
|
||||
_: {
|
||||
client: {start:"_app/immutable/entry/start.Df1GVxyt.js",app:"_app/immutable/entry/app.DL20j9gb.js",imports:["_app/immutable/entry/start.Df1GVxyt.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/entry/app.DL20j9gb.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
||||
nodes: [
|
||||
__memo(() => import('./nodes/0.js')),
|
||||
__memo(() => import('./nodes/1.js')),
|
||||
__memo(() => import('./nodes/2.js')),
|
||||
__memo(() => import('./nodes/3.js')),
|
||||
__memo(() => import('./nodes/4.js')),
|
||||
__memo(() => import('./nodes/5.js')),
|
||||
__memo(() => import('./nodes/6.js')),
|
||||
__memo(() => import('./nodes/7.js')),
|
||||
__memo(() => import('./nodes/8.js')),
|
||||
__memo(() => import('./nodes/9.js')),
|
||||
__memo(() => import('./nodes/10.js')),
|
||||
__memo(() => import('./nodes/11.js')),
|
||||
__memo(() => import('./nodes/12.js')),
|
||||
__memo(() => import('./nodes/13.js')),
|
||||
__memo(() => import('./nodes/14.js'))
|
||||
],
|
||||
remotes: {
|
||||
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
id: "/(app)",
|
||||
pattern: /^\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 3 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/admin",
|
||||
pattern: /^\/admin\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 4 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/api/health",
|
||||
pattern: /^\/api\/health\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/health/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools",
|
||||
pattern: /^\/api\/mappools\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools/[id]",
|
||||
pattern: /^\/api\/mappools\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_id_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools/[id]/slots",
|
||||
pattern: /^\/api\/mappools\/([^/]+?)\/slots\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_id_/slots/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/mappools/[id]/slots/[slotId]",
|
||||
pattern: /^\/api\/mappools\/([^/]+?)\/slots\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false},{"name":"slotId","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/mappools/_id_/slots/_slotId_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches",
|
||||
pattern: /^\/api\/matches\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/lobby",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/lobby\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/lobby/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/pick",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/pick\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/pick/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/rolling",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/rolling\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/rolling/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/roll",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/roll\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/roll/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/matches/[id]/score",
|
||||
pattern: /^\/api\/matches\/([^/]+?)\/score\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/matches/_id_/score/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/osu/beatmap/[id]",
|
||||
pattern: /^\/api\/osu\/beatmap\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/osu/beatmap/_id_/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/api/queue",
|
||||
pattern: /^\/api\/queue\/?$/,
|
||||
params: [],
|
||||
page: null,
|
||||
endpoint: __memo(() => import('./entries/endpoints/api/queue/_server.ts.js'))
|
||||
},
|
||||
{
|
||||
id: "/demo",
|
||||
pattern: /^\/demo\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 11 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/demo/better-auth",
|
||||
pattern: /^\/demo\/better-auth\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 12 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/demo/better-auth/login",
|
||||
pattern: /^\/demo\/better-auth\/login\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 13 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/login",
|
||||
pattern: /^\/login\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,], errors: [1,], leaf: 14 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/mappools",
|
||||
pattern: /^\/mappools\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 5 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/mappools/[id]",
|
||||
pattern: /^\/mappools\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 6 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/matches",
|
||||
pattern: /^\/matches\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 7 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/matches/[id]",
|
||||
pattern: /^\/matches\/([^/]+?)\/?$/,
|
||||
params: [{"name":"id","optional":false,"rest":false,"chained":false}],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 8 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/settings",
|
||||
pattern: /^\/settings\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 9 },
|
||||
endpoint: null
|
||||
},
|
||||
{
|
||||
id: "/(app)/teams",
|
||||
pattern: /^\/teams\/?$/,
|
||||
params: [],
|
||||
page: { layouts: [0,2,], errors: [1,,], leaf: 10 },
|
||||
endpoint: null
|
||||
}
|
||||
],
|
||||
prerendered_routes: new Set([]),
|
||||
matchers: async () => {
|
||||
|
||||
return { };
|
||||
},
|
||||
server_assets: {}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
export const prerendered = new Set([]);
|
||||
|
||||
export const base = "";
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
|
||||
export const index = 0;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/fallbacks/layout.svelte.js')).default;
|
||||
export const imports = ["_app/immutable/nodes/0.9C686m9W.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/CyUoLS3q.js","_app/immutable/chunks/kALFGdUG.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
|
||||
export const index = 1;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/_error.svelte.js')).default;
|
||||
export const imports = ["_app/immutable/nodes/1.RyK1bdTz.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/TpZZa089.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/D2SGgx9j.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/BkIq08el.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/teams/_page.server.ts.js';
|
||||
|
||||
export const index = 10;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/teams/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/teams/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/10.BPlEN6_G.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js","_app/immutable/chunks/CAkKRSSs.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
|
||||
export const index = 11;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/demo/_page.svelte.js')).default;
|
||||
export const imports = ["_app/immutable/nodes/11.DR1Vz7m0.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/TpZZa089.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DoLu9QLy.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/demo/better-auth/_page.server.ts.js';
|
||||
|
||||
export const index = 12;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/demo/better-auth/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/demo/better-auth/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/12.C9UZWAjJ.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/demo/better-auth/login/_page.server.ts.js';
|
||||
|
||||
export const index = 13;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/demo/better-auth/login/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/demo/better-auth/login/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/13.CyZ4e6yK.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,8 +0,0 @@
|
||||
import * as server from '../entries/pages/login/_page.server.ts.js';
|
||||
|
||||
export const index = 14;
|
||||
export { server };
|
||||
export const server_id = "src/routes/login/+page.server.ts";
|
||||
export const imports = [];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/_layout.server.ts.js';
|
||||
|
||||
export const index = 2;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/_layout.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/+layout.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/2.BsR2_q_w.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/CyUoLS3q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js","_app/immutable/chunks/D2SGgx9j.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/BAocuFgQ.js"];
|
||||
export const stylesheets = ["_app/immutable/assets/2.Dv2no-8m.css"];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/_page.server.ts.js';
|
||||
|
||||
export const index = 3;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/3.CMuy9Bx3.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/admin/_page.server.ts.js';
|
||||
|
||||
export const index = 4;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/admin/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/admin/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/4.BdC-uDW-.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js","_app/immutable/chunks/Cy2QBcXt.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/mappools/_page.server.ts.js';
|
||||
|
||||
export const index = 5;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/mappools/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/mappools/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/5.CyqZcxti.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/BnX5ZnVA.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/mappools/_id_/_page.server.ts.js';
|
||||
|
||||
export const index = 6;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/mappools/_id_/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/mappools/[id]/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/6.CEe0czaO.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js","_app/immutable/chunks/CAkKRSSs.js","_app/immutable/chunks/Cy2QBcXt.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/matches/_page.server.ts.js';
|
||||
|
||||
export const index = 7;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/matches/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/matches/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/7._nuD3LlN.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/matches/_id_/_page.server.ts.js';
|
||||
|
||||
export const index = 8;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/matches/_id_/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/matches/[id]/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/8.CkbqikZq.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BLQQas77.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as server from '../entries/pages/(app)/settings/_page.server.ts.js';
|
||||
|
||||
export const index = 9;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/(app)/settings/_page.svelte.js')).default;
|
||||
export { server };
|
||||
export const server_id = "src/routes/(app)/settings/+page.server.ts";
|
||||
export const imports = ["_app/immutable/nodes/9.BTBc_Eli.js","_app/immutable/chunks/DsnmJJEf.js","_app/immutable/chunks/BxEwC6Zc.js","_app/immutable/chunks/DoLu9QLy.js","_app/immutable/chunks/BQKoxf2Q.js","_app/immutable/chunks/kALFGdUG.js","_app/immutable/chunks/BAocuFgQ.js","_app/immutable/chunks/BkIq08el.js","_app/immutable/chunks/CvGSsBh3.js","_app/immutable/chunks/DAtZQVjv.js","_app/immutable/chunks/BnX5ZnVA.js","_app/immutable/chunks/BqOsL1Vv.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
@@ -1,314 +0,0 @@
|
||||
import { get_request_store } from "@sveltejs/kit/internal/server";
|
||||
import { c as create_validator, r as run_remote_function, g as get_cache, a as get_response, p as parse_remote_response } from "./chunks/query.js";
|
||||
import { q } from "./chunks/query.js";
|
||||
import { M as MUTATIVE_METHODS, u as create_field_proxy, v as normalize_issue, w as set_nested_value, x as flatten_issues, y as deep_set, z as stringify_remote_arg, k as stringify } from "./chunks/shared.js";
|
||||
import { ValidationError } from "@sveltejs/kit/internal";
|
||||
import { error, json } from "@sveltejs/kit";
|
||||
import { D as DEV } from "./chunks/false.js";
|
||||
import { b as base, c as app_dir } from "./chunks/server.js";
|
||||
// @__NO_SIDE_EFFECTS__
|
||||
function command(validate_or_fn, maybe_fn) {
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
const validate = create_validator(validate_or_fn, maybe_fn);
|
||||
const __ = { type: "command", id: "", name: "" };
|
||||
const wrapper = (arg) => {
|
||||
const { event, state } = get_request_store();
|
||||
if (!state.allows_commands) {
|
||||
const disallowed_method = !MUTATIVE_METHODS.includes(event.request.method);
|
||||
throw new Error(
|
||||
`Cannot call a command (\`${__.name}(${maybe_fn ? "..." : ""})\`) ${disallowed_method ? `from a ${event.request.method} handler or ` : ""}during server-side rendering`
|
||||
);
|
||||
}
|
||||
state.refreshes ??= {};
|
||||
const promise = Promise.resolve(
|
||||
run_remote_function(event, state, true, () => validate(arg), fn)
|
||||
);
|
||||
promise.updates = () => {
|
||||
throw new Error(`Cannot call '${__.name}(...).updates(...)' on the server`);
|
||||
};
|
||||
return (
|
||||
/** @type {ReturnType<RemoteCommand<Input, Output>>} */
|
||||
promise
|
||||
);
|
||||
};
|
||||
Object.defineProperty(wrapper, "__", { value: __ });
|
||||
Object.defineProperty(wrapper, "pending", {
|
||||
get: () => 0
|
||||
});
|
||||
return wrapper;
|
||||
}
|
||||
// @__NO_SIDE_EFFECTS__
|
||||
function form(validate_or_fn, maybe_fn) {
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
const schema = !maybe_fn || validate_or_fn === "unchecked" ? null : (
|
||||
/** @type {any} */
|
||||
validate_or_fn
|
||||
);
|
||||
function create_instance(key) {
|
||||
const instance = {};
|
||||
instance.method = "POST";
|
||||
Object.defineProperty(instance, "enhance", {
|
||||
value: () => {
|
||||
return { action: instance.action, method: instance.method };
|
||||
}
|
||||
});
|
||||
const __ = {
|
||||
type: "form",
|
||||
name: "",
|
||||
id: "",
|
||||
fn: async (data, meta, form_data) => {
|
||||
const output = {};
|
||||
output.submission = true;
|
||||
const { event, state } = get_request_store();
|
||||
const validated = await schema?.["~standard"].validate(data);
|
||||
if (meta.validate_only) {
|
||||
return validated?.issues?.map((issue) => normalize_issue(issue, true)) ?? [];
|
||||
}
|
||||
if (validated?.issues !== void 0) {
|
||||
handle_issues(output, validated.issues, form_data);
|
||||
} else {
|
||||
if (validated !== void 0) {
|
||||
data = validated.value;
|
||||
}
|
||||
state.refreshes ??= {};
|
||||
const issue = create_issues();
|
||||
try {
|
||||
output.result = await run_remote_function(
|
||||
event,
|
||||
state,
|
||||
true,
|
||||
() => data,
|
||||
(data2) => !maybe_fn ? fn() : fn(data2, issue)
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof ValidationError) {
|
||||
handle_issues(output, e.issues, form_data);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!event.isRemoteRequest) {
|
||||
get_cache(__, state)[""] ??= output;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
Object.defineProperty(instance, "__", { value: __ });
|
||||
Object.defineProperty(instance, "action", {
|
||||
get: () => `?/remote=${__.id}`,
|
||||
enumerable: true
|
||||
});
|
||||
Object.defineProperty(instance, "fields", {
|
||||
get() {
|
||||
return create_field_proxy(
|
||||
{},
|
||||
() => get_cache(__)?.[""]?.input ?? {},
|
||||
(path, value) => {
|
||||
const cache = get_cache(__);
|
||||
const data = cache[""];
|
||||
if (data?.submission) {
|
||||
return;
|
||||
}
|
||||
if (path.length === 0) {
|
||||
(cache[""] ??= {}).input = value;
|
||||
return;
|
||||
}
|
||||
const input = data?.input ?? {};
|
||||
deep_set(input, path.map(String), value);
|
||||
(cache[""] ??= {}).input = input;
|
||||
},
|
||||
() => flatten_issues(get_cache(__)?.[""]?.issues ?? [])
|
||||
);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(instance, "result", {
|
||||
get() {
|
||||
try {
|
||||
return get_cache(__)?.[""]?.result;
|
||||
} catch {
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(instance, "pending", {
|
||||
get: () => 0
|
||||
});
|
||||
Object.defineProperty(instance, "preflight", {
|
||||
// preflight is a noop on the server
|
||||
value: () => instance
|
||||
});
|
||||
Object.defineProperty(instance, "validate", {
|
||||
value: () => {
|
||||
throw new Error("Cannot call validate() on the server");
|
||||
}
|
||||
});
|
||||
if (key == void 0) {
|
||||
Object.defineProperty(instance, "for", {
|
||||
/** @type {RemoteForm<any, any>['for']} */
|
||||
value: (key2) => {
|
||||
const { state } = get_request_store();
|
||||
const cache_key = __.id + "|" + JSON.stringify(key2);
|
||||
let instance2 = (state.form_instances ??= /* @__PURE__ */ new Map()).get(cache_key);
|
||||
if (!instance2) {
|
||||
instance2 = create_instance(key2);
|
||||
instance2.__.id = `${__.id}/${encodeURIComponent(JSON.stringify(key2))}`;
|
||||
instance2.__.name = __.name;
|
||||
state.form_instances.set(cache_key, instance2);
|
||||
}
|
||||
return instance2;
|
||||
}
|
||||
});
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
return create_instance();
|
||||
}
|
||||
function handle_issues(output, issues, form_data) {
|
||||
output.issues = issues.map((issue) => normalize_issue(issue, true));
|
||||
if (form_data) {
|
||||
output.input = {};
|
||||
for (let key of form_data.keys()) {
|
||||
if (/^[.\]]?_/.test(key)) continue;
|
||||
const is_array = key.endsWith("[]");
|
||||
const values = form_data.getAll(key).filter((value) => typeof value === "string");
|
||||
if (is_array) key = key.slice(0, -2);
|
||||
set_nested_value(
|
||||
/** @type {Record<string, any>} */
|
||||
output.input,
|
||||
key,
|
||||
is_array ? values : values[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
function create_issues() {
|
||||
return (
|
||||
/** @type {InvalidField<any>} */
|
||||
new Proxy(
|
||||
/** @param {string} message */
|
||||
(message) => {
|
||||
if (typeof message !== "string") {
|
||||
throw new Error(
|
||||
"`invalid` should now be imported from `@sveltejs/kit` to throw validation issues. The second parameter provided to the form function (renamed to `issue`) is still used to construct issues, e.g. `invalid(issue.field('message'))`. For more info see https://github.com/sveltejs/kit/pulls/14768"
|
||||
);
|
||||
}
|
||||
return create_issue(message);
|
||||
},
|
||||
{
|
||||
get(target, prop) {
|
||||
if (typeof prop === "symbol") return (
|
||||
/** @type {any} */
|
||||
target[prop]
|
||||
);
|
||||
return create_issue_proxy(prop, []);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
function create_issue(message, path = []) {
|
||||
return {
|
||||
message,
|
||||
path
|
||||
};
|
||||
}
|
||||
function create_issue_proxy(key, path) {
|
||||
const new_path = [...path, key];
|
||||
const issue_func = (message) => create_issue(message, new_path);
|
||||
return new Proxy(issue_func, {
|
||||
get(target, prop) {
|
||||
if (typeof prop === "symbol") return (
|
||||
/** @type {any} */
|
||||
target[prop]
|
||||
);
|
||||
if (/^\d+$/.test(prop)) {
|
||||
return create_issue_proxy(parseInt(prop, 10), new_path);
|
||||
}
|
||||
return create_issue_proxy(prop, new_path);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// @__NO_SIDE_EFFECTS__
|
||||
function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
||||
const maybe_fn = typeof fn_or_options === "function" ? fn_or_options : void 0;
|
||||
const options = maybe_options ?? (maybe_fn ? void 0 : fn_or_options);
|
||||
const fn = maybe_fn ?? validate_or_fn;
|
||||
const validate = create_validator(validate_or_fn, maybe_fn);
|
||||
const __ = {
|
||||
type: "prerender",
|
||||
id: "",
|
||||
name: "",
|
||||
has_arg: !!maybe_fn,
|
||||
inputs: options?.inputs,
|
||||
dynamic: options?.dynamic
|
||||
};
|
||||
const wrapper = (arg) => {
|
||||
const promise = (async () => {
|
||||
const { event, state } = get_request_store();
|
||||
const payload = stringify_remote_arg(arg, state.transport);
|
||||
const id = __.id;
|
||||
const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ""}`;
|
||||
if (!state.prerendering && !DEV && !event.isRemoteRequest) {
|
||||
try {
|
||||
return await get_response(__, arg, state, async () => {
|
||||
const key = stringify_remote_arg(arg, state.transport);
|
||||
const cache = get_cache(__, state);
|
||||
const promise3 = cache[key] ??= fetch(new URL(url, event.url.origin).href).then(
|
||||
async (response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Prerendered response not found");
|
||||
}
|
||||
const prerendered = await response.json();
|
||||
if (prerendered.type === "error") {
|
||||
error(prerendered.status, prerendered.error);
|
||||
}
|
||||
return prerendered.result;
|
||||
}
|
||||
);
|
||||
return parse_remote_response(await promise3, state.transport);
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
if (state.prerendering?.remote_responses.has(url)) {
|
||||
return (
|
||||
/** @type {Promise<any>} */
|
||||
state.prerendering.remote_responses.get(url)
|
||||
);
|
||||
}
|
||||
const promise2 = get_response(
|
||||
__,
|
||||
arg,
|
||||
state,
|
||||
() => run_remote_function(event, state, false, () => validate(arg), fn)
|
||||
);
|
||||
if (state.prerendering) {
|
||||
state.prerendering.remote_responses.set(url, promise2);
|
||||
}
|
||||
const result = await promise2;
|
||||
if (state.prerendering) {
|
||||
const body = { type: "result", result: stringify(result, state.transport) };
|
||||
state.prerendering.dependencies.set(url, {
|
||||
body: JSON.stringify(body),
|
||||
response: json(body)
|
||||
});
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
promise.catch(() => {
|
||||
});
|
||||
return (
|
||||
/** @type {RemoteResource<Output>} */
|
||||
promise
|
||||
);
|
||||
};
|
||||
Object.defineProperty(wrapper, "__", { value: __ });
|
||||
return wrapper;
|
||||
}
|
||||
export {
|
||||
command,
|
||||
form,
|
||||
prerender,
|
||||
q as query
|
||||
};
|
||||
14
.svelte-kit/ambient.d.ts
vendored
14
.svelte-kit/ambient.d.ts
vendored
@@ -38,13 +38,6 @@
|
||||
* The above values will be the same _even if_ different values for `ENVIRONMENT` or `PUBLIC_BASE_URL` are set at runtime, as they are statically replaced in your code with their build time values.
|
||||
*/
|
||||
declare module '$env/static/private' {
|
||||
export const DATABASE_URL: string;
|
||||
export const ORIGIN: string;
|
||||
export const BETTER_AUTH_SECRET: string;
|
||||
export const OSU_CLIENT_ID: string;
|
||||
export const OSU_CLIENT_SECRET: string;
|
||||
export const OSU_IRC_USERNAME: string;
|
||||
export const OSU_IRC_PASSWORD: string;
|
||||
export const NVM_INC: string;
|
||||
export const TERM_PROGRAM: string;
|
||||
export const NODE: string;
|
||||
@@ -198,13 +191,6 @@ declare module '$env/static/public' {
|
||||
*/
|
||||
declare module '$env/dynamic/private' {
|
||||
export const env: {
|
||||
DATABASE_URL: string;
|
||||
ORIGIN: string;
|
||||
BETTER_AUTH_SECRET: string;
|
||||
OSU_CLIENT_ID: string;
|
||||
OSU_CLIENT_SECRET: string;
|
||||
OSU_IRC_USERNAME: string;
|
||||
OSU_IRC_PASSWORD: string;
|
||||
NVM_INC: string;
|
||||
TERM_PROGRAM: string;
|
||||
NODE: string;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
export { matchers } from './matchers.js';
|
||||
|
||||
export const nodes = [
|
||||
() => import('./nodes/0'),
|
||||
() => import('./nodes/1'),
|
||||
() => import('./nodes/2'),
|
||||
() => import('./nodes/3'),
|
||||
() => import('./nodes/4'),
|
||||
() => import('./nodes/5'),
|
||||
() => import('./nodes/6'),
|
||||
() => import('./nodes/7'),
|
||||
() => import('./nodes/8'),
|
||||
() => import('./nodes/9'),
|
||||
() => import('./nodes/10'),
|
||||
() => import('./nodes/11'),
|
||||
() => import('./nodes/12'),
|
||||
() => import('./nodes/13'),
|
||||
() => import('./nodes/14')
|
||||
];
|
||||
|
||||
export const server_loads = [2];
|
||||
|
||||
export const dictionary = {
|
||||
"/(app)": [~3,[2]],
|
||||
"/(app)/admin": [~4,[2]],
|
||||
"/demo": [11],
|
||||
"/demo/better-auth": [~12],
|
||||
"/demo/better-auth/login": [~13],
|
||||
"/login": [~14],
|
||||
"/(app)/mappools": [~5,[2]],
|
||||
"/(app)/mappools/[id]": [~6,[2]],
|
||||
"/(app)/matches": [~7,[2]],
|
||||
"/(app)/matches/[id]": [~8,[2]],
|
||||
"/(app)/settings": [~9,[2]],
|
||||
"/(app)/teams": [~10,[2]]
|
||||
};
|
||||
|
||||
export const hooks = {
|
||||
handleError: (({ error }) => { console.error(error) }),
|
||||
|
||||
reroute: (() => {}),
|
||||
transport: {}
|
||||
};
|
||||
|
||||
export const decoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.decode]));
|
||||
export const encoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.encode]));
|
||||
|
||||
export const hash = false;
|
||||
|
||||
export const decode = (type, value) => decoders[type](value);
|
||||
|
||||
export { default as root } from '../root.js';
|
||||
@@ -1 +0,0 @@
|
||||
export const matchers = {};
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-5/layout.svelte";
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../src/routes/+error.svelte";
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../src/routes/(app)/teams/+page.svelte";
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../src/routes/demo/+page.svelte";
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../src/routes/demo/better-auth/+page.svelte";
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../src/routes/demo/better-auth/login/+page.svelte";
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../src/routes/(app)/+layout.svelte";
|
||||
@@ -1 +0,0 @@
|
||||
export { default as component } from "../../../../src/routes/(app)/+page.svelte";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user