WIP. A little custom music server

use oxfmt for everything

+120 -24
AGENTS.md
··· 1 - # Agent Guidelines for Boombox 1 + # Agent Guidelines for Boombox (Monorepo) 2 2 3 - ## Commands 4 - - **Build**: `bun run build` (web), no build needed for backend 5 - - **Dev**: `bun run dev` (root - starts all workspaces), `bun run dev` (individual workspace) 6 - - **Test**: `bun test` (backend), `bun test <file>` (single test) 7 - - **Format**: `bun run format` (web only) 8 - - **DB**: `bun db:migrate`, `bun db:gen` (from root) 3 + This repo is a Bun monorepo with 3 workspaces: 4 + - `backend/`: Bun + Elysia + Effect + Drizzle (SQLite) 5 + - `web/`: Vite + TanStack Start (React) + Tailwind + shadcn/ui 6 + - `shared/`: shared Effect `Schema` types/utilities (`@boombox/shared`) 9 7 10 - ## Code Style 11 - - **Tabs**: Use tabs (4-width), 120 char line length 12 - - **Imports**: Use absolute imports, no barrel files 13 - - **TypeScript**: Strict - no `any`, explicit types preferred 14 - - **Effect**: Use Effect.gen syntax, proper error handling with TaggedError 15 - - **React**: Function components, Jotai for state, shadcn/ui components 16 - - **Naming**: camelCase for variables/functions, PascalCase for components/types 17 - - **Error Handling**: Use Effect error handling, tagged errors for typed failures 8 + If you’re changing only one service, also read the workspace-specific guide: 9 + - Backend: `backend/AGENTS.md` 10 + - Web: `web/AGENTS.md` 18 11 19 - ## Framework Usage 20 - - **Runtime**: Bun (not Node.js) - use `bun` commands, `Bun.file()`, etc. 21 - - **Backend**: Elysia + Effect + Drizzle ORM + SQLite 22 - - **Frontend**: Waku (React framework) + Jotai + UnoCSS + shadcn/ui 23 - - **Database**: SQLite with Drizzle migrations 12 + ## Quick commands (real scripts) 24 13 25 - ## Repository Structure 26 - Monorepo with workspaces: `backend/`, `web/`, `shared/`. Use workspace references (`@boombox/shared`). 14 + ### Install 15 + - **Install all deps**: `bun install` (run from repo root) 27 16 28 - ## Cursor Rules 29 - - Use Bun instead of Node.js, npm, pnpm, or vite. Prefer `bun test`, `bun install`, `Bun.serve()`, etc. 17 + ### Dev 18 + - **Dev (all workspaces)**: `bun run dev` 19 + - This runs `bun --filter '*' dev` from root (starts backend + web). 20 + - **Dev (backend only)**: 21 + - `cd backend && bun run dev` 22 + - or `bun --filter @boombox/backend dev` 23 + - **Dev (web only)**: 24 + - `cd web && bun run dev` (Vite on port `3000`) 25 + - or `bun --filter web-tanstack dev` 26 + 27 + ### Build 28 + - **Build (backend)**: `cd backend && bun run build` 29 + - **Build (web)**: `cd web && bun run build` 30 + 31 + ### Tests (Vitest) 32 + - **Test (backend)**: `cd backend && bun run test` (`vitest run`) 33 + - **Test (web)**: `cd web && bun run test` (`vitest run`) 34 + 35 + #### Run a single test (recommended patterns) 36 + - **Single test file**: 37 + - Backend: `cd backend && bun run test -- test/flac.test.ts` 38 + - Web: `cd web && bun run test -- src/**/some.test.ts` 39 + - **Single test by name** (Vitest `-t`): 40 + - Backend: `cd backend && bun run test -- -t "readMetadata should return album"` 41 + - Web: `cd web && bun run test -- -t "renders"` 42 + - **From repo root via workspace filter** (handy in CI scripts): 43 + - Backend: `bun --filter @boombox/backend test -- test/flac.test.ts` 44 + - Web: `bun --filter web-tanstack test -- -t "something"` 45 + 46 + ### Lint / format 47 + - **Format (all)**: `bun run format` (runs `oxfmt .` from root) 48 + - **Web (lint)**: `cd web && bun run lint` 49 + - **Web (format)**: `cd web && bun run format` 50 + - **Web (format + lint fix)**: `cd web && bun run check` 51 + - **Backend (lint)**: `cd backend && bunx oxlint .` 52 + - **Backend (format)**: `cd backend && bunx oxfmt --write .` 53 + 54 + ### Database 55 + - **Migrate (root)**: `bun db:migrate` (runs `backend` migrate script) 56 + - **Generate migrations (root)**: `bun db:gen` (runs `drizzle-kit generate` in backend) 57 + 58 + ## Repo structure & how code is organized 59 + 60 + ### Workspaces 61 + - **`backend/`**: API + ingestion + DB layer 62 + - Effect-first architecture: services via `Effect.Service`, composed with `Layer` 63 + - Elysia routes run Effects via `ManagedRuntime.runPromise` 64 + - SQLite is used both via Drizzle ORM (queries) and direct Bun SQLite migrator usage for migrations 65 + - **`web/`**: React app 66 + - TanStack Start router (file-based routes) + TanStack Query integration 67 + - Tailwind CSS styling 68 + - “shadcn/ui” components live under `web/src/components/ui` 69 + - **`shared/`**: shared Effect `Schema` models/brands and utilities 70 + 71 + ### Import paths (no barrels) 72 + - **Backend alias**: `~/*` → `backend/src/*` (see `backend/tsconfig.json`) 73 + - **Web alias**: `@/*` → `web/src/*` (see `web/tsconfig.json`) 74 + - **Shared**: import as `@boombox/shared` (workspace dependency) 75 + - **Rule of thumb**: prefer explicit module imports (no barrel files), and keep imports stable (avoid deep relative paths like `../../../../x`). 76 + 77 + ## Code style (enforced by config) 78 + 79 + ### Formatting 80 + - **Formatter**: oxfmt (Oxc formatter) 81 + - **Tabs**: `tabWidth = 4`, `useTabs = true` 82 + - **Line length**: `printWidth = 120` 83 + - **Semicolons**: enabled 84 + - Run `bun run format` from repo root to format everything 85 + 86 + ### TypeScript 87 + - **Strict**: all workspaces have `"strict": true` 88 + - **Avoid `any`**: prefer explicit types or `unknown` + decoding 89 + - **Runtime validation**: 90 + - Prefer `effect/Schema` for boundaries (DB rows → API DTOs, env vars, external inputs) 91 + - Use `Schema.decodeUnknown` / `Schema.transformOrFail` for safe transformations 92 + 93 + ### Naming & files 94 + - **Types / classes**: `PascalCase` (`AlbumNotFoundError`, `ApiService`) 95 + - **Functions / variables**: `camelCase` (`getAlbumById`, `syncLibraryStream`) 96 + - **React components**: `PascalCase` exported components; filenames tend to be kebab-case (`album-card.tsx`, `song-row.tsx`) 97 + - **Constants**: `SCREAMING_SNAKE_CASE` for “true constants” (`SHUTDOWN_TIMEOUT_MS`) 98 + 99 + ## Error handling (Effect-first) 100 + 101 + ### Prefer typed failures 102 + - **Create domain errors** using Effect’s tagged errors: 103 + - `Data.TaggedError("MyError")<{ ... }>` 104 + - or `Schema.TaggedError<...>()("MyError", { ...schema... })` when you want schema-validated payloads 105 + - **Handle failures explicitly**: 106 + - `Effect.catchTag("MyError", ...)` for tagged errors 107 + - `Effect.mapError(...)` to translate lower-level errors into domain errors 108 + - `Effect.tapError(Effect.logError)` / `Effect.tapErrorCause(...)` for logging without changing control flow 109 + 110 + ### Don’t throw for control flow (except at framework boundaries) 111 + - In pure Effect code, prefer `Effect.fail(...)` / `Effect.fromNullable` rather than `throw`. 112 + - In Elysia route handlers, throwing is acceptable only for *immediate request validation* or framework-level early exits; otherwise return an Effect and map errors to HTTP status codes. 113 + 114 + ## Cursor / editor rules 115 + 116 + ### Bun-first policy 117 + - Use **Bun** for installs/scripts/tests. Avoid npm/pnpm/yarn. 118 + 119 + ### Cursor rules found in repo 120 + - `web/.cursorrules` includes shadcn guidance (uses `pnpx shadcn@latest add <component>`). Follow it when adding shadcn components. 121 + 122 + ## “Agent ergonomics” (how to work safely here) 123 + - Keep changes scoped to the relevant workspace (`backend/` vs `web/`). 124 + - When editing API contracts used by web, update both sides (backend `ApiType` + web client usage). 125 + - Prefer adding/adjusting tests close to the code (`backend/test/*`, `backend/src/**.test.ts`, `web/**.test.ts`).
-8
backend/.prettierrc.toml
··· 1 - tabWidth = 4 2 - useTabs = true 3 - printWidth = 120 4 - semi = true 5 - experimentalOperatorPosition = "start" 6 - quoteProps = "consistent" 7 - objectWrap = "preserve" 8 - bracketSameLine = false
+118
backend/AGENTS.md
··· 1 + # Backend Agent Guide (`@boombox/backend`) 2 + 3 + This workspace is the Bun API + ingestion service built with **Elysia + Effect + Drizzle (SQLite)**. 4 + 5 + If you haven’t yet, read the repo entrypoint first: `../AGENTS.md`. 6 + 7 + ## Commands (run from `backend/`) 8 + 9 + - **Dev (watch)**: `bun run dev` 10 + - Runs `bun run --watch ./src/index.ts` 11 + - **Build**: `bun run build` 12 + - Builds `./src/index.ts` to `./dist` (`bun build --target=bun --bytecode`) 13 + - **Test**: `bun run test` 14 + - Runs `vitest run` 15 + 16 + ### Single test execution 17 + 18 + - **Single file**: `bun run test -- test/flac.test.ts` 19 + - **By name**: `bun run test -- -t "parseFile should parse flac just fine"` 20 + - **Watch mode (Vitest)**: `bunx vitest` or `bunx vitest --watch` 21 + 22 + ### Database 23 + 24 + - **Migrate**: `bun run db:migrate` 25 + - Runs `src/db/migrate.ts` 26 + - **Generate migration**: `bun run db:gen` 27 + - Runs `drizzle-kit generate` 28 + 29 + ### Lint / format 30 + 31 + - **Lint (oxlint)**: `bunx oxlint .` 32 + - **Format (oxfmt)**: `bunx oxfmt .` 33 + 34 + ## Entry points & folder map 35 + 36 + - **`src/index.ts`**: runtime + layers + startup/shutdown + periodic sync loop 37 + - **`src/api.ts`**: Elysia server + Effect-powered handlers + typed API surface 38 + - **`src/db/`**: 39 + - `schema.ts`: Drizzle schema & relations 40 + - `index.ts`: Effect SQL/Drizzle service layer (`DatabaseLive`) 41 + - `migrate.ts`: migration runner 42 + - **`src/file-parser.ts`**: stream-based directory scan + file parsing per extension 43 + - **`src/sync-library.ts`**: batching + inserts + relation wiring 44 + - **`src/flac/`, `src/m4a/`**: format-specific metadata readers (Effect services + errors) 45 + - **`test/`**: Vitest tests (some use `@effect/vitest`) 46 + - **`test-data/`**: fixture audio files 47 + 48 + ## Coding patterns (preferred) 49 + 50 + ### Effect services + layers 51 + 52 + Use `Effect.Service` and compose layers rather than manually passing dependencies. 53 + 54 + - Prefer: 55 + - `class X extends Effect.Service<X>()("...", { dependencies: [...], accessors: true, effect: Effect.gen(...) }) {}` 56 + - `Layer.merge / Layer.mergeAll / Layer.provideMerge` to assemble the app layer 57 + 58 + ### “Effect-first” function style 59 + 60 + - Prefer `Effect.gen(function* () { ... })` for sequencing. 61 + - Use `Effect.fn("label")(function* (...) { ... })` for named effects (better tracing/logging). 62 + - Use `pipe(...)` only when it improves readability; don’t over-nest. 63 + 64 + ### Typed errors (no untyped throws in core logic) 65 + 66 + - Define domain failures with `Data.TaggedError(...)` or `Schema.TaggedError(...)`. 67 + - When you need to change the error domain: 68 + - `Effect.mapError((cause) => new MyDomainError({ ... }))` 69 + - When you need to handle a specific failure: 70 + - `Effect.catchTag("MyDomainError", (e) => ...)` 71 + 72 + Good examples already in the codebase: 73 + 74 + - `src/api.ts`: `FileNotFoundError`, `AlbumNotFoundError` (tagged) and `Effect.catchTag(...)` in route handlers 75 + - `src/m4a/errors.ts`: schema-backed tagged errors for structured payloads 76 + 77 + ### Schema at boundaries (decode/transform) 78 + 79 + Use `effect/Schema` for: 80 + 81 + - Env/config validation (`src/utils/env.ts`) 82 + - DB row → API DTO transformations (`Schema.transformOrFail`, `Schema.decodeUnknown`) 83 + 84 + If you’re returning data from DB queries, prefer: 85 + 86 + - `Schema.decodeUnknown(...)` on the raw DB row(s) 87 + - then transform to your public response schema 88 + 89 + ## Elysia + Effect integration 90 + 91 + ### Handler rule of thumb 92 + 93 + - **Inside handlers**: build an `Effect` value, then execute via `runtime.runPromise`. 94 + - **Error mapping**: don’t rely on “whatever runtime throws”: 95 + - translate domain errors to HTTP status with `Effect.catchTag(...)` and return `elysia/status(...)` 96 + 97 + `backend/ISSUES.md` calls out that error handling is currently inconsistent; if you touch `src/api.ts`, consider improving handler wrappers rather than adding more one-off mappings. 98 + 99 + ## Concurrency & SQLite notes 100 + 101 + SQLite will lock under write-heavy concurrent load. 102 + 103 + - In streams that write to DB, prefer `Stream.mapEffect(fn, { concurrency: 1 })` unless you’ve verified safe parallelism. 104 + - Batch inserts with `Stream.grouped(n)` (see `src/sync-library.ts`). 105 + 106 + ## Config & ports 107 + 108 + - `DB_URL` and `FOLDER_PATH` are required env vars (see `src/utils/env.ts`). 109 + - The API currently listens on port `3003` in `src/api.ts` and `src/index.ts` logs that port. 110 + - `backend/ISSUES.md` suggests making port configurable via `Config`. 111 + 112 + ## Testing conventions 113 + 114 + This workspace uses Vitest, plus `@effect/vitest` in several tests. 115 + 116 + - For Effect workflows, prefer `test.effect("name", () => Effect.gen(...))`. 117 + - Provide dependencies with `Effect.provide(SomeLayer)` (see `backend/test/*.test.ts`). 118 + - For plain Vitest `it(...)`, wrap effect execution with `ManagedRuntime.runPromise(...)` when needed.
+11 -12
backend/src/api.ts
··· 123 123 new AlbumNotFoundError({ 124 124 message: "Album not found", 125 125 cause: e, 126 - }) 127 - ) 126 + }), 127 + ), 128 128 ); 129 129 130 130 yield* Effect.log(album); ··· 200 200 artists, 201 201 musicbrainz: row.musicbrainz ?? undefined, 202 202 }); 203 - }) 203 + }), 204 204 ); 205 205 206 206 return yield* Effect.all(result); ··· 216 216 new FileNotFoundError({ 217 217 message: `Failed to find file with id ${id}`, 218 218 cause: { id }, 219 - }) 219 + }), 220 220 ); 221 221 } 222 222 ··· 230 230 new FileNotFoundError({ 231 231 message: `File not found on disk: ${file.path}`, 232 232 cause: { id, path: file.path }, 233 - }) 233 + }), 234 234 ); 235 235 } 236 236 237 237 const filename = file.path.split("/").pop(); 238 238 const encodedFilename = encodeURIComponent(filename ?? ""); 239 239 240 - set.headers[ 241 - "Content-Disposition" 242 - ] = `attachment; filename="${encodedFilename}"; filename*=UTF-8''${encodedFilename}`; 240 + set.headers["Content-Disposition"] = 241 + `attachment; filename="${encodedFilename}"; filename*=UTF-8''${encodedFilename}`; 243 242 set.headers["Content-Type"] = "audio/flac"; 244 243 set.headers["Accept-Ranges"] = "bytes"; 245 244 ··· 258 257 .innerJoin(albumTable, eq(songTable.albumId, albumTable.id)) 259 258 .innerJoin(songToArtistTable, eq(songToArtistTable.songId, songTable.id)) 260 259 .innerJoin(artistTable, eq(songToArtistTable.artistId, artistTable.id)) 261 - .all() 260 + .all(), 262 261 ); 263 262 264 263 const reduced = rows.reduce<Record<string, GetSongType>>((acc, row) => { ··· 325 324 query: t.Object({ 326 325 include: t.Optional(t.Array(t.String())), 327 326 }), 328 - } 327 + }, 329 328 ) 330 329 331 330 .get( ··· 334 333 pipe( 335 334 ApiService.getFileById(id, set), 336 335 Effect.catchTag("FileNotFoundError", (e) => Effect.succeed(status(404, e.message))), 337 - runtime.runPromise 336 + runtime.runPromise, 338 337 ), 339 338 { 340 339 params: t.Object({ 341 340 id: t.String(), 342 341 }), 343 - } 342 + }, 344 343 ) 345 344 .get("/songs", () => pipe(ApiService.getAllSongs(), runtime.runPromise)); 346 345
+1 -1
backend/src/db/index.ts
··· 22 22 schemaDirectory: "./drizzle", 23 23 }), 24 24 Layer.provide(SqliteLive), 25 - Layer.provide(BunFileSystem.layer) 25 + Layer.provide(BunFileSystem.layer), 26 26 );
+5 -5
backend/src/file-parser.ts
··· 52 52 (metadata): ParseResult => ({ 53 53 _tag: "success", 54 54 metadata, 55 - }) 55 + }), 56 56 ), 57 57 Effect.catchAll((err) => 58 58 Effect.succeed<ParseResult>({ ··· 61 61 path: file, 62 62 error: err instanceof Error ? err.message : String(err), 63 63 }, 64 - }) 65 - ) 64 + }), 65 + ), 66 66 ), 67 - { concurrency: 10 } 67 + { concurrency: 10 }, 68 68 ), 69 69 Stream.tap((result) => { 70 70 progress++; ··· 79 79 return Console.log(`Progress: ${progress}/${total} (${((progress / total) * 100).toFixed(1)}%)`); 80 80 } 81 81 return Effect.void; 82 - }) 82 + }), 83 83 ); 84 84 85 85 return stream;
+4 -4
backend/src/index.ts
··· 13 13 const AppLayer = Layer.merge(FlacService.Default, M4aService.Default).pipe( 14 14 Layer.provideMerge(DatabaseLive.Default), 15 15 Layer.provideMerge(BunContext.layer), 16 - Layer.merge(OtelLive) 16 + Layer.merge(OtelLive), 17 17 ); 18 18 19 19 const SHUTDOWN_TIMEOUT_MS = 10_000; ··· 50 50 Effect.gen(function* () { 51 51 yield* Effect.log(`Received ${signal}, starting graceful shutdown...`); 52 52 yield* Deferred.succeed(shutdownSignal, undefined); 53 - }).pipe(Effect.provide(AppLayer)) 53 + }).pipe(Effect.provide(AppLayer)), 54 54 ); 55 55 }; 56 56 ··· 64 64 // Wrap with error handling so failures are logged and don't silently stop the repeat loop 65 65 const syncWithErrorHandling = syncLibraryStream(folderPath).pipe( 66 66 Effect.tapErrorCause((cause) => 67 - Effect.log(`Sync failed with cause: ${cause}`).pipe(Effect.annotateLogs("level", "error")) 67 + Effect.log(`Sync failed with cause: ${cause}`).pipe(Effect.annotateLogs("level", "error")), 68 68 ), 69 - Effect.catchAllCause((cause) => Effect.log(`Sync run failed, will retry on next schedule. Cause: ${cause}`)) 69 + Effect.catchAllCause((cause) => Effect.log(`Sync run failed, will retry on next schedule. Cause: ${cause}`)), 70 70 ); 71 71 72 72 const syncFiber = yield* Effect.fork(syncWithErrorHandling.pipe(Effect.repeat(Schedule.spaced("3 minutes"))));
+1 -1
backend/src/lib.ts
··· 30 30 ); 31 31 } 32 32 return yield* Effect.succeed(buffer); 33 - }); 33 + });
+5 -5
backend/src/m4a/service.test.ts
··· 16 16 const input = nonM4aFilePath; 17 17 const actual = yield* Effect.exit(m4aService.readMetadata(input)); 18 18 expect(Exit.isFailure(actual)).toBe(true); 19 - }) 19 + }), 20 20 )); 21 21 22 22 it("readMetadata should NOT throw an error on a valid m4a file", () => ··· 26 26 const input = m4aFilePath; 27 27 const actual = yield* Effect.exit(m4aService.readMetadata(input)); 28 28 expect(Exit.isSuccess(actual)).toBe(true); 29 - }) 29 + }), 30 30 )); 31 31 32 32 it("readMetadata should return album, artist and title", () => ··· 41 41 expect(actual.artists).toContain("Foster the People"); 42 42 expect(actual.title).toBe("Downtown"); 43 43 expect(actual.trackNumber).toBe(15); 44 - }) 44 + }), 45 45 )); 46 46 47 47 it("readMetadata should be able to parse musicbrainz tags", () => ··· 54 54 expect(actual.musicBrainzReleaseGroupId).toBe("535748f7-5b3d-4a2a-8f9c-bd8baa587239"); 55 55 expect(actual.musicBrainzArtistId).toBe("e0e1a584-dd0a-4bd1-88d1-c4c62895039d"); 56 56 expect(actual.musicBrainzTrackId).toBe("be8931c6-f360-41eb-8a19-1ca97342cdb4"); 57 - }) 57 + }), 58 58 )); 59 59 60 60 const m4aFilePathNoMusicBrainz = "./test-data/no-musicbrainz.m4a"; ··· 68 68 expect(actual.musicBrainzReleaseGroupId).toBeUndefined(); 69 69 expect(actual.musicBrainzArtistId).toBeUndefined(); 70 70 expect(actual.musicBrainzTrackId).toBeUndefined(); 71 - }) 71 + }), 72 72 ));
+1 -1
backend/src/m4a/service.ts
··· 199 199 200 200 return yield* decoder(result); 201 201 }, 202 - (effect) => Effect.scoped(effect) 202 + (effect) => Effect.scoped(effect), 203 203 ); 204 204 205 205 return {
+1 -1
backend/src/m4a/transformers.ts
··· 145 145 const trackView = new DataView( 146 146 valueBytes.buffer, 147 147 valueBytes.byteOffset, 148 - valueBytes.byteLength 148 + valueBytes.byteLength, 149 149 ); 150 150 metadata.trackNumber = trackView.getUint16(2); 151 151 }
+5 -5
backend/test/flac.test.ts
··· 10 10 const inputNonFlac = "./test-data/01 - hover.mp3"; 11 11 const actualNonFlac = yield* flacService.readMetadata(inputNonFlac).pipe( 12 12 Effect.as(true), 13 - Effect.catchTag("FlacError", (_) => Effect.succeed(false)) 13 + Effect.catchTag("FlacError", (_) => Effect.succeed(false)), 14 14 ); 15 15 16 16 expect(actualNonFlac).toBe(false); 17 - }).pipe(provide) 17 + }).pipe(provide), 18 18 ); 19 19 20 20 test.effect("readMetadata should NOT error if file is FLAC", () => ··· 23 23 const input = "./test-data/11 - Tropical Fish.flac"; 24 24 const actual = yield* flacService.readMetadata(input).pipe( 25 25 Effect.as(true), 26 - Effect.catchTag("FlacError", (_) => Effect.succeed(false)) 26 + Effect.catchTag("FlacError", (_) => Effect.succeed(false)), 27 27 ); 28 28 29 29 expect(actual).toBe(true); 30 - }).pipe(provide) 30 + }).pipe(provide), 31 31 ); 32 32 33 33 test.effect("readMetadata should return album, artist and title", () => ··· 39 39 expect(actual.album).toContain("midnight cruisin"); 40 40 expect(actual.album).toContain("MUGSHOT"); 41 41 expect(actual).toHaveProperty("title", "TROPICAL FISH"); 42 - }).pipe(provide) 42 + }).pipe(provide), 43 43 );
+2 -2
backend/vitest.config.ts
··· 2 2 import tsconfigPaths from "vite-tsconfig-paths"; 3 3 4 4 export default defineConfig({ 5 - plugins: [tsconfigPaths()] 6 - }); 5 + plugins: [tsconfigPaths()], 6 + });
-38
bun.lock
··· 93 93 "zustand": "^5.0.8", 94 94 }, 95 95 "devDependencies": { 96 - "@prettier/plugin-oxc": "^0.0.4", 97 96 "@tanstack/eslint-config": "^0.3.0", 98 97 "@testing-library/dom": "^10.4.0", 99 98 "@testing-library/react": "^16.2.0", ··· 103 102 "@vitejs/plugin-react": "^5.0.4", 104 103 "babel-plugin-react-compiler": "^1.0.0", 105 104 "jsdom": "^27.0.0", 106 - "prettier": "^3.5.3", 107 105 "typescript": "^5.7.2", 108 106 "vite": "^7.1.7", 109 107 "vitest": "^3.0.5", ··· 394 392 395 393 "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.38.0", "", {}, "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg=="], 396 394 397 - "@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.74.0", "", { "os": "android", "cpu": "arm64" }, "sha512-lgq8TJq22eyfojfa2jBFy2m66ckAo7iNRYDdyn9reXYA3I6Wx7tgGWVx1JAp1lO+aUiqdqP/uPlDaETL9tqRcg=="], 398 - 399 - "@oxc-parser/binding-darwin-arm64": ["@oxc-parser/binding-darwin-arm64@0.74.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xbY/io/hkARggbpYEMFX6CwFzb7f4iS6WuBoBeZtdqRWfIEi7sm/uYWXfyVeB8uqOATvJ07WRFC2upI8PSI83g=="], 400 - 401 - "@oxc-parser/binding-darwin-x64": ["@oxc-parser/binding-darwin-x64@0.74.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-FIj2gAGtFaW0Zk+TnGyenMUoRu1ju+kJ/h71D77xc1owOItbFZFGa+4WSVck1H8rTtceeJlK+kux+vCjGFCl9Q=="], 402 - 403 - "@oxc-parser/binding-freebsd-x64": ["@oxc-parser/binding-freebsd-x64@0.74.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-W1I+g5TJg0TRRMHgEWNWsTIfe782V3QuaPgZxnfPNmDMywYdtlzllzclBgaDq6qzvZCCQc/UhvNb37KWTCTj8A=="], 404 - 405 - "@oxc-parser/binding-linux-arm-gnueabihf": ["@oxc-parser/binding-linux-arm-gnueabihf@0.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-gxqkyRGApeVI8dgvJ19SYe59XASW3uVxF1YUgkE7peW/XIg5QRAOVTFKyTjI9acYuK1MF6OJHqx30cmxmZLtiQ=="], 406 - 407 - "@oxc-parser/binding-linux-arm-musleabihf": ["@oxc-parser/binding-linux-arm-musleabihf@0.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jpnAUP4Fa93VdPPDzxxBguJmldj/Gpz7wTXKFzpAueqBMfZsy9KNC+0qT2uZ9HGUDMzNuKw0Se3bPCpL/gfD2Q=="], 408 - 409 - "@oxc-parser/binding-linux-arm64-gnu": ["@oxc-parser/binding-linux-arm64-gnu@0.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-fcWyM7BNfCkHqIf3kll8fJctbR/PseL4RnS2isD9Y3FFBhp4efGAzhDaxIUK5GK7kIcFh1P+puIRig8WJ6IMVQ=="], 410 - 411 - "@oxc-parser/binding-linux-arm64-musl": ["@oxc-parser/binding-linux-arm64-musl@0.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AMY30z/C77HgiRRJX7YtVUaelKq1ex0aaj28XoJu4SCezdS8i0IftUNTtGS1UzGjGZB8zQz5SFwVy4dRu4GLwg=="], 412 - 413 - "@oxc-parser/binding-linux-riscv64-gnu": ["@oxc-parser/binding-linux-riscv64-gnu@0.74.0", "", { "os": "linux", "cpu": "none" }, "sha512-/RZAP24TgZo4vV/01TBlzRqs0R7E6xvatww4LnmZEBBulQBU/SkypDywfriFqWuFoa61WFXPV7sLcTjJGjim/w=="], 414 - 415 - "@oxc-parser/binding-linux-s390x-gnu": ["@oxc-parser/binding-linux-s390x-gnu@0.74.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-620J1beNAlGSPBD+Msb3ptvrwxu04B8iULCH03zlf0JSLy/5sqlD6qBs0XUVkUJv1vbakUw1gfVnUQqv0UTuEg=="], 416 - 417 - "@oxc-parser/binding-linux-x64-gnu": ["@oxc-parser/binding-linux-x64-gnu@0.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WBFgQmGtFnPNzHyLKbC1wkYGaRIBxXGofO0+hz1xrrkPgbxbJS1Ukva1EB8sPaVBBQ52Bdc2GjLSp721NWRvww=="], 418 - 419 - "@oxc-parser/binding-linux-x64-musl": ["@oxc-parser/binding-linux-x64-musl@0.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-y4mapxi0RGqlp3t6Sm+knJlAEqdKDYrEue2LlXOka/F2i4sRN0XhEMPiSOB3ppHmvK4I2zY2XBYTsX1Fel0fAg=="], 420 - 421 - "@oxc-parser/binding-wasm32-wasi": ["@oxc-parser/binding-wasm32-wasi@0.74.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-yDS9bRDh5ymobiS2xBmjlrGdUuU61IZoJBaJC5fELdYT5LJNBXlbr3Yc6m2PWfRJwkH6Aq5fRvxAZ4wCbkGa8w=="], 422 - 423 - "@oxc-parser/binding-win32-arm64-msvc": ["@oxc-parser/binding-win32-arm64-msvc@0.74.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-XFWY52Rfb4N5wEbMCTSBMxRkDLGbAI9CBSL24BIDywwDJMl31gHEVlmHdCDRoXAmanCI6gwbXYTrWe0HvXJ7Aw=="], 424 - 425 - "@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.74.0", "", { "os": "win32", "cpu": "x64" }, "sha512-1D3x6iU2apLyfTQHygbdaNbX3nZaHu4yaXpD7ilYpoLo7f0MX0tUuoDrqJyJrVGqvyXgc0uz4yXz9tH9ZZhvvg=="], 426 - 427 - "@oxc-project/types": ["@oxc-project/types@0.74.0", "", {}, "sha512-KOw/RZrVlHGhCXh1RufBFF7Nuo7HdY5w1lRJukM/igIl6x9qtz8QycDvZdzb4qnHO7znrPyo2sJrFJK2eKHgfQ=="], 428 - 429 395 "@oxfmt/darwin-arm64": ["@oxfmt/darwin-arm64@0.16.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-I+Unj7wePcUTK7p/YKtgbm4yer6dw7dTlmCJa0UilFZyge5uD4rwCSfSDx3A+a6Z3A60/SqXMbNR2UyidWF4Cg=="], 430 396 431 397 "@oxfmt/darwin-x64": ["@oxfmt/darwin-x64@0.16.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-EfiXFKEOV5gXgEatFK89OOoSmd8E9Xq83TcjPLWQNFBO4cgaQsfKmctpgJmJjQnoUwD7nQsm0ruj3ae7Gva8QA=="], ··· 485 451 "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="], 486 452 487 453 "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], 488 - 489 - "@prettier/plugin-oxc": ["@prettier/plugin-oxc@0.0.4", "", { "dependencies": { "oxc-parser": "0.74.0" } }, "sha512-UGXe+g/rSRbglL0FOJiar+a+nUrst7KaFmsg05wYbKiInGWP6eAj/f8A2Uobgo5KxEtb2X10zeflNH6RK2xeIQ=="], 490 454 491 455 "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], 492 456 ··· 1307 1271 "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], 1308 1272 1309 1273 "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], 1310 - 1311 - "oxc-parser": ["oxc-parser@0.74.0", "", { "dependencies": { "@oxc-project/types": "^0.74.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm64": "0.74.0", "@oxc-parser/binding-darwin-arm64": "0.74.0", "@oxc-parser/binding-darwin-x64": "0.74.0", "@oxc-parser/binding-freebsd-x64": "0.74.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.74.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.74.0", "@oxc-parser/binding-linux-arm64-gnu": "0.74.0", "@oxc-parser/binding-linux-arm64-musl": "0.74.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.74.0", "@oxc-parser/binding-linux-s390x-gnu": "0.74.0", "@oxc-parser/binding-linux-x64-gnu": "0.74.0", "@oxc-parser/binding-linux-x64-musl": "0.74.0", "@oxc-parser/binding-wasm32-wasi": "0.74.0", "@oxc-parser/binding-win32-arm64-msvc": "0.74.0", "@oxc-parser/binding-win32-x64-msvc": "0.74.0" } }, "sha512-2tDN/ttU8WE6oFh8EzKNam7KE7ZXSG5uXmvX85iNzxdJfMssDWcj3gpYzZi1E04XuE7m3v1dVWl/8BE886vPGw=="], 1312 1274 1313 1275 "oxfmt": ["oxfmt@0.16.0", "", { "optionalDependencies": { "@oxfmt/darwin-arm64": "0.16.0", "@oxfmt/darwin-x64": "0.16.0", "@oxfmt/linux-arm64-gnu": "0.16.0", "@oxfmt/linux-arm64-musl": "0.16.0", "@oxfmt/linux-x64-gnu": "0.16.0", "@oxfmt/linux-x64-musl": "0.16.0", "@oxfmt/win32-arm64": "0.16.0", "@oxfmt/win32-x64": "0.16.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-uRnnBAN0zH07FXSfvSKbIw+Jrohv4Px2RwNiZOGI4/pvns4sx0+k4WSt+tqwd7bDeoWaXiGmhZgnbK63hi6hVQ=="], 1314 1276
+2 -1
package.json
··· 9 9 "scripts": { 10 10 "dev": "bun --filter '*' dev", 11 11 "db:migrate": "bun --filter @boombox/backend db:migrate", 12 - "db:gen": "bun --filter @boombox/backend db:gen" 12 + "db:gen": "bun --filter @boombox/backend db:gen", 13 + "format": "bunx oxfmt ." 13 14 }, 14 15 "devDependencies": { 15 16 "oxfmt": "^0.16.0"
+1 -1
vitest.config.js
··· 1 1 import { defineConfig } from "vitest/config"; 2 2 import tsconfigPaths from "vite-tsconfig-paths"; 3 3 export default defineConfig({ 4 - plugins: [tsconfigPaths()], 4 + plugins: [tsconfigPaths()], 5 5 });
-11
web/.prettierrc.toml
··· 1 - 2 - tabWidth = 4 3 - useTabs = true 4 - printWidth = 120 5 - semi = true 6 - experimentalOperatorPosition = "start" 7 - quoteProps = "consistent" 8 - objectWrap = "preserve" 9 - bracketSameLine = false 10 - 11 - plugins = ['@prettier/plugin-oxc']
+131
web/AGENTS.md
··· 1 + # Web Agent Guide (`web-tanstack`) 2 + 3 + This workspace is a **Vite + TanStack Start (React)** app with Tailwind + shadcn/ui. 4 + 5 + If you haven’t yet, read the repo entrypoint first: `../AGENTS.md`. 6 + 7 + ## Commands (run from `web/`) 8 + 9 + - **Dev**: `bun run dev` 10 + - Runs `vite dev --port 3000` 11 + - **Build**: `bun run build` 12 + - Runs `vite build` 13 + - **Preview**: `bun run serve` 14 + - Runs `vite preview` 15 + - **Test**: `bun run test` 16 + - Runs `vitest run` 17 + - **Lint**: `bun run lint` 18 + - Runs `eslint` (TanStack ESLint config) 19 + - **Format**: `bun run format` 20 + - Runs `oxfmt .` 21 + - **Format + lint fix**: `bun run check` 22 + - Runs `oxfmt . && eslint --fix` 23 + 24 + ### Single test execution (Vitest) 25 + 26 + - **Single file**: `bun run test -- src/**/some.test.ts` 27 + - **By name**: `bun run test -- -t "renders"` 28 + - **Watch mode**: `bunx vitest` 29 + 30 + ## Project map 31 + 32 + - **`src/routes/`**: file-based routing (TanStack Router) 33 + - `__root.tsx`: root document shell (Head, Scripts, devtools, global UI like `AudioPlayer`) 34 + - `index.tsx`: starter homepage (can be replaced) 35 + - `album/*`: album pages 36 + - **`src/router.tsx`**: router creation + SSR query integration wrapper 37 + - **`src/integrations/tanstack-query/`**: query client/provider integration 38 + - **`src/components/`**: app components 39 + - `components/ui/`: shadcn/ui components (do not hand-edit unless needed) 40 + - `components/player/`: audio player UI 41 + - **`src/lib/`**: shared web utilities (API client, errors, utils) 42 + - **`src/store/`**: global state (currently Zustand) 43 + 44 + ## Imports, TypeScript, and formatting 45 + 46 + ### Imports 47 + 48 + - Use the **`@/*`** alias for `src/*` (configured in `web/tsconfig.json`). 49 + - Prefer `import type { ... }` for type-only imports (important when importing backend types). 50 + - Avoid barrel files; import from the exact module. 51 + 52 + ### TypeScript expectations 53 + 54 + - `"strict": true` and unused checks are enabled (`noUnusedLocals`, `noUnusedParameters`). 55 + - Prefer explicit return types for exported functions when it clarifies behavior. 56 + - Use `effect/Schema` when you need runtime validation on untrusted data. 57 + 58 + ### Formatting rules 59 + 60 + Uses oxfmt (Oxc formatter): 61 + 62 + - tabs, `tabWidth = 4` 63 + - `printWidth = 120` 64 + - semicolons enabled 65 + 66 + ## UI + styling rules 67 + 68 + ### Tailwind 69 + 70 + - Tailwind is the primary styling system. Prefer utility classes over bespoke CSS. 71 + - Shared CSS entry is `src/styles.css`. 72 + 73 + ### shadcn/ui 74 + 75 + - UI primitives are under `src/components/ui/` and are typically managed by the shadcn CLI. 76 + - Repo Cursor rule: `web/.cursorrules` says to add new components with: 77 + - `pnpx shadcn@latest add <component>` 78 + 79 + Even though the repo is Bun-first, follow that shadcn instruction when installing shadcn components (it matches shadcn’s documented workflow). 80 + 81 + ## Data fetching & API integration 82 + 83 + ### Typed backend client (Elysia Eden) 84 + 85 + The web client is typed against the backend’s Elysia server type: 86 + 87 + - `src/lib/api.ts` exports `client = treaty<ApiType>("localhost:3003")` 88 + 89 + Guidelines: 90 + 91 + - Keep backend type imports as `type` imports. 92 + - Don’t hardcode fetch URLs in multiple places; route everything through the `client` (or a small wrapper in `src/lib/`). 93 + - If you change backend routes/response shapes, update the web usage in the same PR. 94 + 95 + ### TanStack Query + Router integration 96 + 97 + - Router is created in `src/router.tsx`. 98 + - Query integration lives in `src/integrations/tanstack-query/`. 99 + - Prefer loaders / query hooks where TanStack Start expects them; keep side effects inside effects/hooks. 100 + 101 + ## State management (Zustand) 102 + 103 + The app currently uses Zustand (not Jotai) for global state: 104 + 105 + - Example: `src/store/player.ts` uses `persist` middleware to store volume in localStorage. 106 + 107 + Guidelines: 108 + 109 + - Keep stores small and domain-focused (one store per concern). 110 + - Prefer derived selectors in components rather than storing redundant computed state. 111 + 112 + ## Error handling 113 + 114 + ### Prefer typed errors for domain failures 115 + 116 + The web workspace already defines Effect tagged errors in `src/lib/errors.ts`. 117 + 118 + - When modeling a failure that you intend to handle, prefer `Data.TaggedError("...")<{ ... }>` 119 + - When calling async functions, fail early with a typed error and surface it at the component boundary. 120 + 121 + ### UI surfacing 122 + 123 + - For user-visible errors, prefer a clear empty state + retry action. 124 + - For dev-only errors, console logging is OK, but don’t ship noisy logs in hot paths. 125 + 126 + ## Testing guidance 127 + 128 + Vitest is set up for the workspace. 129 + 130 + - Prefer Testing Library for component tests (`@testing-library/react` is installed). 131 + - For pure utilities, write plain unit tests under `src/**/__tests__` or alongside the module.
+2 -4
web/package.json
··· 8 8 "serve": "vite preview", 9 9 "test": "vitest run", 10 10 "lint": "eslint", 11 - "format": "prettier", 12 - "check": "prettier --write . && eslint --fix" 11 + "format": "bunx oxfmt .", 12 + "check": "bunx oxfmt . && eslint --fix" 13 13 }, 14 14 "dependencies": { 15 15 "@elysiajs/eden": "^1.4.4", ··· 42 42 "zustand": "^5.0.8" 43 43 }, 44 44 "devDependencies": { 45 - "@prettier/plugin-oxc": "^0.0.4", 46 45 "@tanstack/eslint-config": "^0.3.0", 47 46 "@testing-library/dom": "^10.4.0", 48 47 "@testing-library/react": "^16.2.0", ··· 52 51 "@vitejs/plugin-react": "^5.0.4", 53 52 "babel-plugin-react-compiler": "^1.0.0", 54 53 "jsdom": "^27.0.0", 55 - "prettier": "^3.5.3", 56 54 "typescript": "^5.7.2", 57 55 "vite": "^7.1.7", 58 56 "vitest": "^3.0.5",
+321 -323
web/src/routeTree.gen.ts
··· 8 8 // You should NOT make any changes in this file as it will be overwritten. 9 9 // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. 10 10 11 - import { Route as rootRouteImport } from './routes/__root' 12 - import { Route as McpRouteImport } from './routes/mcp' 13 - import { Route as IndexRouteImport } from './routes/index' 14 - import { Route as AlbumIndexRouteImport } from './routes/album/index' 15 - import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query' 16 - import { Route as DemoMcpTodosRouteImport } from './routes/demo/mcp-todos' 17 - import { Route as AlbumAlbumIdRouteImport } from './routes/album/$albumId' 18 - import { Route as DemoStartServerFuncsRouteImport } from './routes/demo/start.server-funcs' 19 - import { Route as DemoStartApiRequestRouteImport } from './routes/demo/start.api-request' 20 - import { Route as DemoApiTqTodosRouteImport } from './routes/demo/api.tq-todos' 21 - import { Route as DemoApiNamesRouteImport } from './routes/demo/api.names' 22 - import { Route as DemoApiMcpTodosRouteImport } from './routes/demo/api.mcp-todos' 23 - import { Route as DemoStartSsrIndexRouteImport } from './routes/demo/start.ssr.index' 24 - import { Route as DemoStartSsrSpaModeRouteImport } from './routes/demo/start.ssr.spa-mode' 25 - import { Route as DemoStartSsrFullSsrRouteImport } from './routes/demo/start.ssr.full-ssr' 26 - import { Route as DemoStartSsrDataOnlyRouteImport } from './routes/demo/start.ssr.data-only' 11 + import { Route as rootRouteImport } from "./routes/__root"; 12 + import { Route as McpRouteImport } from "./routes/mcp"; 13 + import { Route as IndexRouteImport } from "./routes/index"; 14 + import { Route as AlbumIndexRouteImport } from "./routes/album/index"; 15 + import { Route as DemoTanstackQueryRouteImport } from "./routes/demo/tanstack-query"; 16 + import { Route as DemoMcpTodosRouteImport } from "./routes/demo/mcp-todos"; 17 + import { Route as AlbumAlbumIdRouteImport } from "./routes/album/$albumId"; 18 + import { Route as DemoStartServerFuncsRouteImport } from "./routes/demo/start.server-funcs"; 19 + import { Route as DemoStartApiRequestRouteImport } from "./routes/demo/start.api-request"; 20 + import { Route as DemoApiTqTodosRouteImport } from "./routes/demo/api.tq-todos"; 21 + import { Route as DemoApiNamesRouteImport } from "./routes/demo/api.names"; 22 + import { Route as DemoApiMcpTodosRouteImport } from "./routes/demo/api.mcp-todos"; 23 + import { Route as DemoStartSsrIndexRouteImport } from "./routes/demo/start.ssr.index"; 24 + import { Route as DemoStartSsrSpaModeRouteImport } from "./routes/demo/start.ssr.spa-mode"; 25 + import { Route as DemoStartSsrFullSsrRouteImport } from "./routes/demo/start.ssr.full-ssr"; 26 + import { Route as DemoStartSsrDataOnlyRouteImport } from "./routes/demo/start.ssr.data-only"; 27 27 28 28 const McpRoute = McpRouteImport.update({ 29 - id: '/mcp', 30 - path: '/mcp', 31 - getParentRoute: () => rootRouteImport, 32 - } as any) 29 + id: "/mcp", 30 + path: "/mcp", 31 + getParentRoute: () => rootRouteImport, 32 + } as any); 33 33 const IndexRoute = IndexRouteImport.update({ 34 - id: '/', 35 - path: '/', 36 - getParentRoute: () => rootRouteImport, 37 - } as any) 34 + id: "/", 35 + path: "/", 36 + getParentRoute: () => rootRouteImport, 37 + } as any); 38 38 const AlbumIndexRoute = AlbumIndexRouteImport.update({ 39 - id: '/album/', 40 - path: '/album/', 41 - getParentRoute: () => rootRouteImport, 42 - } as any) 39 + id: "/album/", 40 + path: "/album/", 41 + getParentRoute: () => rootRouteImport, 42 + } as any); 43 43 const DemoTanstackQueryRoute = DemoTanstackQueryRouteImport.update({ 44 - id: '/demo/tanstack-query', 45 - path: '/demo/tanstack-query', 46 - getParentRoute: () => rootRouteImport, 47 - } as any) 44 + id: "/demo/tanstack-query", 45 + path: "/demo/tanstack-query", 46 + getParentRoute: () => rootRouteImport, 47 + } as any); 48 48 const DemoMcpTodosRoute = DemoMcpTodosRouteImport.update({ 49 - id: '/demo/mcp-todos', 50 - path: '/demo/mcp-todos', 51 - getParentRoute: () => rootRouteImport, 52 - } as any) 49 + id: "/demo/mcp-todos", 50 + path: "/demo/mcp-todos", 51 + getParentRoute: () => rootRouteImport, 52 + } as any); 53 53 const AlbumAlbumIdRoute = AlbumAlbumIdRouteImport.update({ 54 - id: '/album/$albumId', 55 - path: '/album/$albumId', 56 - getParentRoute: () => rootRouteImport, 57 - } as any) 54 + id: "/album/$albumId", 55 + path: "/album/$albumId", 56 + getParentRoute: () => rootRouteImport, 57 + } as any); 58 58 const DemoStartServerFuncsRoute = DemoStartServerFuncsRouteImport.update({ 59 - id: '/demo/start/server-funcs', 60 - path: '/demo/start/server-funcs', 61 - getParentRoute: () => rootRouteImport, 62 - } as any) 59 + id: "/demo/start/server-funcs", 60 + path: "/demo/start/server-funcs", 61 + getParentRoute: () => rootRouteImport, 62 + } as any); 63 63 const DemoStartApiRequestRoute = DemoStartApiRequestRouteImport.update({ 64 - id: '/demo/start/api-request', 65 - path: '/demo/start/api-request', 66 - getParentRoute: () => rootRouteImport, 67 - } as any) 64 + id: "/demo/start/api-request", 65 + path: "/demo/start/api-request", 66 + getParentRoute: () => rootRouteImport, 67 + } as any); 68 68 const DemoApiTqTodosRoute = DemoApiTqTodosRouteImport.update({ 69 - id: '/demo/api/tq-todos', 70 - path: '/demo/api/tq-todos', 71 - getParentRoute: () => rootRouteImport, 72 - } as any) 69 + id: "/demo/api/tq-todos", 70 + path: "/demo/api/tq-todos", 71 + getParentRoute: () => rootRouteImport, 72 + } as any); 73 73 const DemoApiNamesRoute = DemoApiNamesRouteImport.update({ 74 - id: '/demo/api/names', 75 - path: '/demo/api/names', 76 - getParentRoute: () => rootRouteImport, 77 - } as any) 74 + id: "/demo/api/names", 75 + path: "/demo/api/names", 76 + getParentRoute: () => rootRouteImport, 77 + } as any); 78 78 const DemoApiMcpTodosRoute = DemoApiMcpTodosRouteImport.update({ 79 - id: '/demo/api/mcp-todos', 80 - path: '/demo/api/mcp-todos', 81 - getParentRoute: () => rootRouteImport, 82 - } as any) 79 + id: "/demo/api/mcp-todos", 80 + path: "/demo/api/mcp-todos", 81 + getParentRoute: () => rootRouteImport, 82 + } as any); 83 83 const DemoStartSsrIndexRoute = DemoStartSsrIndexRouteImport.update({ 84 - id: '/demo/start/ssr/', 85 - path: '/demo/start/ssr/', 86 - getParentRoute: () => rootRouteImport, 87 - } as any) 84 + id: "/demo/start/ssr/", 85 + path: "/demo/start/ssr/", 86 + getParentRoute: () => rootRouteImport, 87 + } as any); 88 88 const DemoStartSsrSpaModeRoute = DemoStartSsrSpaModeRouteImport.update({ 89 - id: '/demo/start/ssr/spa-mode', 90 - path: '/demo/start/ssr/spa-mode', 91 - getParentRoute: () => rootRouteImport, 92 - } as any) 89 + id: "/demo/start/ssr/spa-mode", 90 + path: "/demo/start/ssr/spa-mode", 91 + getParentRoute: () => rootRouteImport, 92 + } as any); 93 93 const DemoStartSsrFullSsrRoute = DemoStartSsrFullSsrRouteImport.update({ 94 - id: '/demo/start/ssr/full-ssr', 95 - path: '/demo/start/ssr/full-ssr', 96 - getParentRoute: () => rootRouteImport, 97 - } as any) 94 + id: "/demo/start/ssr/full-ssr", 95 + path: "/demo/start/ssr/full-ssr", 96 + getParentRoute: () => rootRouteImport, 97 + } as any); 98 98 const DemoStartSsrDataOnlyRoute = DemoStartSsrDataOnlyRouteImport.update({ 99 - id: '/demo/start/ssr/data-only', 100 - path: '/demo/start/ssr/data-only', 101 - getParentRoute: () => rootRouteImport, 102 - } as any) 99 + id: "/demo/start/ssr/data-only", 100 + path: "/demo/start/ssr/data-only", 101 + getParentRoute: () => rootRouteImport, 102 + } as any); 103 103 104 104 export interface FileRoutesByFullPath { 105 - '/': typeof IndexRoute 106 - '/mcp': typeof McpRoute 107 - '/album/$albumId': typeof AlbumAlbumIdRoute 108 - '/demo/mcp-todos': typeof DemoMcpTodosRoute 109 - '/demo/tanstack-query': typeof DemoTanstackQueryRoute 110 - '/album': typeof AlbumIndexRoute 111 - '/demo/api/mcp-todos': typeof DemoApiMcpTodosRoute 112 - '/demo/api/names': typeof DemoApiNamesRoute 113 - '/demo/api/tq-todos': typeof DemoApiTqTodosRoute 114 - '/demo/start/api-request': typeof DemoStartApiRequestRoute 115 - '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute 116 - '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute 117 - '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute 118 - '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute 119 - '/demo/start/ssr': typeof DemoStartSsrIndexRoute 105 + "/": typeof IndexRoute; 106 + "/mcp": typeof McpRoute; 107 + "/album/$albumId": typeof AlbumAlbumIdRoute; 108 + "/demo/mcp-todos": typeof DemoMcpTodosRoute; 109 + "/demo/tanstack-query": typeof DemoTanstackQueryRoute; 110 + "/album": typeof AlbumIndexRoute; 111 + "/demo/api/mcp-todos": typeof DemoApiMcpTodosRoute; 112 + "/demo/api/names": typeof DemoApiNamesRoute; 113 + "/demo/api/tq-todos": typeof DemoApiTqTodosRoute; 114 + "/demo/start/api-request": typeof DemoStartApiRequestRoute; 115 + "/demo/start/server-funcs": typeof DemoStartServerFuncsRoute; 116 + "/demo/start/ssr/data-only": typeof DemoStartSsrDataOnlyRoute; 117 + "/demo/start/ssr/full-ssr": typeof DemoStartSsrFullSsrRoute; 118 + "/demo/start/ssr/spa-mode": typeof DemoStartSsrSpaModeRoute; 119 + "/demo/start/ssr": typeof DemoStartSsrIndexRoute; 120 120 } 121 121 export interface FileRoutesByTo { 122 - '/': typeof IndexRoute 123 - '/mcp': typeof McpRoute 124 - '/album/$albumId': typeof AlbumAlbumIdRoute 125 - '/demo/mcp-todos': typeof DemoMcpTodosRoute 126 - '/demo/tanstack-query': typeof DemoTanstackQueryRoute 127 - '/album': typeof AlbumIndexRoute 128 - '/demo/api/mcp-todos': typeof DemoApiMcpTodosRoute 129 - '/demo/api/names': typeof DemoApiNamesRoute 130 - '/demo/api/tq-todos': typeof DemoApiTqTodosRoute 131 - '/demo/start/api-request': typeof DemoStartApiRequestRoute 132 - '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute 133 - '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute 134 - '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute 135 - '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute 136 - '/demo/start/ssr': typeof DemoStartSsrIndexRoute 122 + "/": typeof IndexRoute; 123 + "/mcp": typeof McpRoute; 124 + "/album/$albumId": typeof AlbumAlbumIdRoute; 125 + "/demo/mcp-todos": typeof DemoMcpTodosRoute; 126 + "/demo/tanstack-query": typeof DemoTanstackQueryRoute; 127 + "/album": typeof AlbumIndexRoute; 128 + "/demo/api/mcp-todos": typeof DemoApiMcpTodosRoute; 129 + "/demo/api/names": typeof DemoApiNamesRoute; 130 + "/demo/api/tq-todos": typeof DemoApiTqTodosRoute; 131 + "/demo/start/api-request": typeof DemoStartApiRequestRoute; 132 + "/demo/start/server-funcs": typeof DemoStartServerFuncsRoute; 133 + "/demo/start/ssr/data-only": typeof DemoStartSsrDataOnlyRoute; 134 + "/demo/start/ssr/full-ssr": typeof DemoStartSsrFullSsrRoute; 135 + "/demo/start/ssr/spa-mode": typeof DemoStartSsrSpaModeRoute; 136 + "/demo/start/ssr": typeof DemoStartSsrIndexRoute; 137 137 } 138 138 export interface FileRoutesById { 139 - __root__: typeof rootRouteImport 140 - '/': typeof IndexRoute 141 - '/mcp': typeof McpRoute 142 - '/album/$albumId': typeof AlbumAlbumIdRoute 143 - '/demo/mcp-todos': typeof DemoMcpTodosRoute 144 - '/demo/tanstack-query': typeof DemoTanstackQueryRoute 145 - '/album/': typeof AlbumIndexRoute 146 - '/demo/api/mcp-todos': typeof DemoApiMcpTodosRoute 147 - '/demo/api/names': typeof DemoApiNamesRoute 148 - '/demo/api/tq-todos': typeof DemoApiTqTodosRoute 149 - '/demo/start/api-request': typeof DemoStartApiRequestRoute 150 - '/demo/start/server-funcs': typeof DemoStartServerFuncsRoute 151 - '/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute 152 - '/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute 153 - '/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute 154 - '/demo/start/ssr/': typeof DemoStartSsrIndexRoute 139 + __root__: typeof rootRouteImport; 140 + "/": typeof IndexRoute; 141 + "/mcp": typeof McpRoute; 142 + "/album/$albumId": typeof AlbumAlbumIdRoute; 143 + "/demo/mcp-todos": typeof DemoMcpTodosRoute; 144 + "/demo/tanstack-query": typeof DemoTanstackQueryRoute; 145 + "/album/": typeof AlbumIndexRoute; 146 + "/demo/api/mcp-todos": typeof DemoApiMcpTodosRoute; 147 + "/demo/api/names": typeof DemoApiNamesRoute; 148 + "/demo/api/tq-todos": typeof DemoApiTqTodosRoute; 149 + "/demo/start/api-request": typeof DemoStartApiRequestRoute; 150 + "/demo/start/server-funcs": typeof DemoStartServerFuncsRoute; 151 + "/demo/start/ssr/data-only": typeof DemoStartSsrDataOnlyRoute; 152 + "/demo/start/ssr/full-ssr": typeof DemoStartSsrFullSsrRoute; 153 + "/demo/start/ssr/spa-mode": typeof DemoStartSsrSpaModeRoute; 154 + "/demo/start/ssr/": typeof DemoStartSsrIndexRoute; 155 155 } 156 156 export interface FileRouteTypes { 157 - fileRoutesByFullPath: FileRoutesByFullPath 158 - fullPaths: 159 - | '/' 160 - | '/mcp' 161 - | '/album/$albumId' 162 - | '/demo/mcp-todos' 163 - | '/demo/tanstack-query' 164 - | '/album' 165 - | '/demo/api/mcp-todos' 166 - | '/demo/api/names' 167 - | '/demo/api/tq-todos' 168 - | '/demo/start/api-request' 169 - | '/demo/start/server-funcs' 170 - | '/demo/start/ssr/data-only' 171 - | '/demo/start/ssr/full-ssr' 172 - | '/demo/start/ssr/spa-mode' 173 - | '/demo/start/ssr' 174 - fileRoutesByTo: FileRoutesByTo 175 - to: 176 - | '/' 177 - | '/mcp' 178 - | '/album/$albumId' 179 - | '/demo/mcp-todos' 180 - | '/demo/tanstack-query' 181 - | '/album' 182 - | '/demo/api/mcp-todos' 183 - | '/demo/api/names' 184 - | '/demo/api/tq-todos' 185 - | '/demo/start/api-request' 186 - | '/demo/start/server-funcs' 187 - | '/demo/start/ssr/data-only' 188 - | '/demo/start/ssr/full-ssr' 189 - | '/demo/start/ssr/spa-mode' 190 - | '/demo/start/ssr' 191 - id: 192 - | '__root__' 193 - | '/' 194 - | '/mcp' 195 - | '/album/$albumId' 196 - | '/demo/mcp-todos' 197 - | '/demo/tanstack-query' 198 - | '/album/' 199 - | '/demo/api/mcp-todos' 200 - | '/demo/api/names' 201 - | '/demo/api/tq-todos' 202 - | '/demo/start/api-request' 203 - | '/demo/start/server-funcs' 204 - | '/demo/start/ssr/data-only' 205 - | '/demo/start/ssr/full-ssr' 206 - | '/demo/start/ssr/spa-mode' 207 - | '/demo/start/ssr/' 208 - fileRoutesById: FileRoutesById 157 + fileRoutesByFullPath: FileRoutesByFullPath; 158 + fullPaths: 159 + | "/" 160 + | "/mcp" 161 + | "/album/$albumId" 162 + | "/demo/mcp-todos" 163 + | "/demo/tanstack-query" 164 + | "/album" 165 + | "/demo/api/mcp-todos" 166 + | "/demo/api/names" 167 + | "/demo/api/tq-todos" 168 + | "/demo/start/api-request" 169 + | "/demo/start/server-funcs" 170 + | "/demo/start/ssr/data-only" 171 + | "/demo/start/ssr/full-ssr" 172 + | "/demo/start/ssr/spa-mode" 173 + | "/demo/start/ssr"; 174 + fileRoutesByTo: FileRoutesByTo; 175 + to: 176 + | "/" 177 + | "/mcp" 178 + | "/album/$albumId" 179 + | "/demo/mcp-todos" 180 + | "/demo/tanstack-query" 181 + | "/album" 182 + | "/demo/api/mcp-todos" 183 + | "/demo/api/names" 184 + | "/demo/api/tq-todos" 185 + | "/demo/start/api-request" 186 + | "/demo/start/server-funcs" 187 + | "/demo/start/ssr/data-only" 188 + | "/demo/start/ssr/full-ssr" 189 + | "/demo/start/ssr/spa-mode" 190 + | "/demo/start/ssr"; 191 + id: 192 + | "__root__" 193 + | "/" 194 + | "/mcp" 195 + | "/album/$albumId" 196 + | "/demo/mcp-todos" 197 + | "/demo/tanstack-query" 198 + | "/album/" 199 + | "/demo/api/mcp-todos" 200 + | "/demo/api/names" 201 + | "/demo/api/tq-todos" 202 + | "/demo/start/api-request" 203 + | "/demo/start/server-funcs" 204 + | "/demo/start/ssr/data-only" 205 + | "/demo/start/ssr/full-ssr" 206 + | "/demo/start/ssr/spa-mode" 207 + | "/demo/start/ssr/"; 208 + fileRoutesById: FileRoutesById; 209 209 } 210 210 export interface RootRouteChildren { 211 - IndexRoute: typeof IndexRoute 212 - McpRoute: typeof McpRoute 213 - AlbumAlbumIdRoute: typeof AlbumAlbumIdRoute 214 - DemoMcpTodosRoute: typeof DemoMcpTodosRoute 215 - DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute 216 - AlbumIndexRoute: typeof AlbumIndexRoute 217 - DemoApiMcpTodosRoute: typeof DemoApiMcpTodosRoute 218 - DemoApiNamesRoute: typeof DemoApiNamesRoute 219 - DemoApiTqTodosRoute: typeof DemoApiTqTodosRoute 220 - DemoStartApiRequestRoute: typeof DemoStartApiRequestRoute 221 - DemoStartServerFuncsRoute: typeof DemoStartServerFuncsRoute 222 - DemoStartSsrDataOnlyRoute: typeof DemoStartSsrDataOnlyRoute 223 - DemoStartSsrFullSsrRoute: typeof DemoStartSsrFullSsrRoute 224 - DemoStartSsrSpaModeRoute: typeof DemoStartSsrSpaModeRoute 225 - DemoStartSsrIndexRoute: typeof DemoStartSsrIndexRoute 211 + IndexRoute: typeof IndexRoute; 212 + McpRoute: typeof McpRoute; 213 + AlbumAlbumIdRoute: typeof AlbumAlbumIdRoute; 214 + DemoMcpTodosRoute: typeof DemoMcpTodosRoute; 215 + DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute; 216 + AlbumIndexRoute: typeof AlbumIndexRoute; 217 + DemoApiMcpTodosRoute: typeof DemoApiMcpTodosRoute; 218 + DemoApiNamesRoute: typeof DemoApiNamesRoute; 219 + DemoApiTqTodosRoute: typeof DemoApiTqTodosRoute; 220 + DemoStartApiRequestRoute: typeof DemoStartApiRequestRoute; 221 + DemoStartServerFuncsRoute: typeof DemoStartServerFuncsRoute; 222 + DemoStartSsrDataOnlyRoute: typeof DemoStartSsrDataOnlyRoute; 223 + DemoStartSsrFullSsrRoute: typeof DemoStartSsrFullSsrRoute; 224 + DemoStartSsrSpaModeRoute: typeof DemoStartSsrSpaModeRoute; 225 + DemoStartSsrIndexRoute: typeof DemoStartSsrIndexRoute; 226 226 } 227 227 228 - declare module '@tanstack/react-router' { 229 - interface FileRoutesByPath { 230 - '/mcp': { 231 - id: '/mcp' 232 - path: '/mcp' 233 - fullPath: '/mcp' 234 - preLoaderRoute: typeof McpRouteImport 235 - parentRoute: typeof rootRouteImport 236 - } 237 - '/': { 238 - id: '/' 239 - path: '/' 240 - fullPath: '/' 241 - preLoaderRoute: typeof IndexRouteImport 242 - parentRoute: typeof rootRouteImport 243 - } 244 - '/album/': { 245 - id: '/album/' 246 - path: '/album' 247 - fullPath: '/album' 248 - preLoaderRoute: typeof AlbumIndexRouteImport 249 - parentRoute: typeof rootRouteImport 250 - } 251 - '/demo/tanstack-query': { 252 - id: '/demo/tanstack-query' 253 - path: '/demo/tanstack-query' 254 - fullPath: '/demo/tanstack-query' 255 - preLoaderRoute: typeof DemoTanstackQueryRouteImport 256 - parentRoute: typeof rootRouteImport 257 - } 258 - '/demo/mcp-todos': { 259 - id: '/demo/mcp-todos' 260 - path: '/demo/mcp-todos' 261 - fullPath: '/demo/mcp-todos' 262 - preLoaderRoute: typeof DemoMcpTodosRouteImport 263 - parentRoute: typeof rootRouteImport 264 - } 265 - '/album/$albumId': { 266 - id: '/album/$albumId' 267 - path: '/album/$albumId' 268 - fullPath: '/album/$albumId' 269 - preLoaderRoute: typeof AlbumAlbumIdRouteImport 270 - parentRoute: typeof rootRouteImport 271 - } 272 - '/demo/start/server-funcs': { 273 - id: '/demo/start/server-funcs' 274 - path: '/demo/start/server-funcs' 275 - fullPath: '/demo/start/server-funcs' 276 - preLoaderRoute: typeof DemoStartServerFuncsRouteImport 277 - parentRoute: typeof rootRouteImport 278 - } 279 - '/demo/start/api-request': { 280 - id: '/demo/start/api-request' 281 - path: '/demo/start/api-request' 282 - fullPath: '/demo/start/api-request' 283 - preLoaderRoute: typeof DemoStartApiRequestRouteImport 284 - parentRoute: typeof rootRouteImport 285 - } 286 - '/demo/api/tq-todos': { 287 - id: '/demo/api/tq-todos' 288 - path: '/demo/api/tq-todos' 289 - fullPath: '/demo/api/tq-todos' 290 - preLoaderRoute: typeof DemoApiTqTodosRouteImport 291 - parentRoute: typeof rootRouteImport 292 - } 293 - '/demo/api/names': { 294 - id: '/demo/api/names' 295 - path: '/demo/api/names' 296 - fullPath: '/demo/api/names' 297 - preLoaderRoute: typeof DemoApiNamesRouteImport 298 - parentRoute: typeof rootRouteImport 299 - } 300 - '/demo/api/mcp-todos': { 301 - id: '/demo/api/mcp-todos' 302 - path: '/demo/api/mcp-todos' 303 - fullPath: '/demo/api/mcp-todos' 304 - preLoaderRoute: typeof DemoApiMcpTodosRouteImport 305 - parentRoute: typeof rootRouteImport 306 - } 307 - '/demo/start/ssr/': { 308 - id: '/demo/start/ssr/' 309 - path: '/demo/start/ssr' 310 - fullPath: '/demo/start/ssr' 311 - preLoaderRoute: typeof DemoStartSsrIndexRouteImport 312 - parentRoute: typeof rootRouteImport 313 - } 314 - '/demo/start/ssr/spa-mode': { 315 - id: '/demo/start/ssr/spa-mode' 316 - path: '/demo/start/ssr/spa-mode' 317 - fullPath: '/demo/start/ssr/spa-mode' 318 - preLoaderRoute: typeof DemoStartSsrSpaModeRouteImport 319 - parentRoute: typeof rootRouteImport 320 - } 321 - '/demo/start/ssr/full-ssr': { 322 - id: '/demo/start/ssr/full-ssr' 323 - path: '/demo/start/ssr/full-ssr' 324 - fullPath: '/demo/start/ssr/full-ssr' 325 - preLoaderRoute: typeof DemoStartSsrFullSsrRouteImport 326 - parentRoute: typeof rootRouteImport 327 - } 328 - '/demo/start/ssr/data-only': { 329 - id: '/demo/start/ssr/data-only' 330 - path: '/demo/start/ssr/data-only' 331 - fullPath: '/demo/start/ssr/data-only' 332 - preLoaderRoute: typeof DemoStartSsrDataOnlyRouteImport 333 - parentRoute: typeof rootRouteImport 334 - } 335 - } 228 + declare module "@tanstack/react-router" { 229 + interface FileRoutesByPath { 230 + "/mcp": { 231 + id: "/mcp"; 232 + path: "/mcp"; 233 + fullPath: "/mcp"; 234 + preLoaderRoute: typeof McpRouteImport; 235 + parentRoute: typeof rootRouteImport; 236 + }; 237 + "/": { 238 + id: "/"; 239 + path: "/"; 240 + fullPath: "/"; 241 + preLoaderRoute: typeof IndexRouteImport; 242 + parentRoute: typeof rootRouteImport; 243 + }; 244 + "/album/": { 245 + id: "/album/"; 246 + path: "/album"; 247 + fullPath: "/album"; 248 + preLoaderRoute: typeof AlbumIndexRouteImport; 249 + parentRoute: typeof rootRouteImport; 250 + }; 251 + "/demo/tanstack-query": { 252 + id: "/demo/tanstack-query"; 253 + path: "/demo/tanstack-query"; 254 + fullPath: "/demo/tanstack-query"; 255 + preLoaderRoute: typeof DemoTanstackQueryRouteImport; 256 + parentRoute: typeof rootRouteImport; 257 + }; 258 + "/demo/mcp-todos": { 259 + id: "/demo/mcp-todos"; 260 + path: "/demo/mcp-todos"; 261 + fullPath: "/demo/mcp-todos"; 262 + preLoaderRoute: typeof DemoMcpTodosRouteImport; 263 + parentRoute: typeof rootRouteImport; 264 + }; 265 + "/album/$albumId": { 266 + id: "/album/$albumId"; 267 + path: "/album/$albumId"; 268 + fullPath: "/album/$albumId"; 269 + preLoaderRoute: typeof AlbumAlbumIdRouteImport; 270 + parentRoute: typeof rootRouteImport; 271 + }; 272 + "/demo/start/server-funcs": { 273 + id: "/demo/start/server-funcs"; 274 + path: "/demo/start/server-funcs"; 275 + fullPath: "/demo/start/server-funcs"; 276 + preLoaderRoute: typeof DemoStartServerFuncsRouteImport; 277 + parentRoute: typeof rootRouteImport; 278 + }; 279 + "/demo/start/api-request": { 280 + id: "/demo/start/api-request"; 281 + path: "/demo/start/api-request"; 282 + fullPath: "/demo/start/api-request"; 283 + preLoaderRoute: typeof DemoStartApiRequestRouteImport; 284 + parentRoute: typeof rootRouteImport; 285 + }; 286 + "/demo/api/tq-todos": { 287 + id: "/demo/api/tq-todos"; 288 + path: "/demo/api/tq-todos"; 289 + fullPath: "/demo/api/tq-todos"; 290 + preLoaderRoute: typeof DemoApiTqTodosRouteImport; 291 + parentRoute: typeof rootRouteImport; 292 + }; 293 + "/demo/api/names": { 294 + id: "/demo/api/names"; 295 + path: "/demo/api/names"; 296 + fullPath: "/demo/api/names"; 297 + preLoaderRoute: typeof DemoApiNamesRouteImport; 298 + parentRoute: typeof rootRouteImport; 299 + }; 300 + "/demo/api/mcp-todos": { 301 + id: "/demo/api/mcp-todos"; 302 + path: "/demo/api/mcp-todos"; 303 + fullPath: "/demo/api/mcp-todos"; 304 + preLoaderRoute: typeof DemoApiMcpTodosRouteImport; 305 + parentRoute: typeof rootRouteImport; 306 + }; 307 + "/demo/start/ssr/": { 308 + id: "/demo/start/ssr/"; 309 + path: "/demo/start/ssr"; 310 + fullPath: "/demo/start/ssr"; 311 + preLoaderRoute: typeof DemoStartSsrIndexRouteImport; 312 + parentRoute: typeof rootRouteImport; 313 + }; 314 + "/demo/start/ssr/spa-mode": { 315 + id: "/demo/start/ssr/spa-mode"; 316 + path: "/demo/start/ssr/spa-mode"; 317 + fullPath: "/demo/start/ssr/spa-mode"; 318 + preLoaderRoute: typeof DemoStartSsrSpaModeRouteImport; 319 + parentRoute: typeof rootRouteImport; 320 + }; 321 + "/demo/start/ssr/full-ssr": { 322 + id: "/demo/start/ssr/full-ssr"; 323 + path: "/demo/start/ssr/full-ssr"; 324 + fullPath: "/demo/start/ssr/full-ssr"; 325 + preLoaderRoute: typeof DemoStartSsrFullSsrRouteImport; 326 + parentRoute: typeof rootRouteImport; 327 + }; 328 + "/demo/start/ssr/data-only": { 329 + id: "/demo/start/ssr/data-only"; 330 + path: "/demo/start/ssr/data-only"; 331 + fullPath: "/demo/start/ssr/data-only"; 332 + preLoaderRoute: typeof DemoStartSsrDataOnlyRouteImport; 333 + parentRoute: typeof rootRouteImport; 334 + }; 335 + } 336 336 } 337 337 338 338 const rootRouteChildren: RootRouteChildren = { 339 - IndexRoute: IndexRoute, 340 - McpRoute: McpRoute, 341 - AlbumAlbumIdRoute: AlbumAlbumIdRoute, 342 - DemoMcpTodosRoute: DemoMcpTodosRoute, 343 - DemoTanstackQueryRoute: DemoTanstackQueryRoute, 344 - AlbumIndexRoute: AlbumIndexRoute, 345 - DemoApiMcpTodosRoute: DemoApiMcpTodosRoute, 346 - DemoApiNamesRoute: DemoApiNamesRoute, 347 - DemoApiTqTodosRoute: DemoApiTqTodosRoute, 348 - DemoStartApiRequestRoute: DemoStartApiRequestRoute, 349 - DemoStartServerFuncsRoute: DemoStartServerFuncsRoute, 350 - DemoStartSsrDataOnlyRoute: DemoStartSsrDataOnlyRoute, 351 - DemoStartSsrFullSsrRoute: DemoStartSsrFullSsrRoute, 352 - DemoStartSsrSpaModeRoute: DemoStartSsrSpaModeRoute, 353 - DemoStartSsrIndexRoute: DemoStartSsrIndexRoute, 354 - } 355 - export const routeTree = rootRouteImport 356 - ._addFileChildren(rootRouteChildren) 357 - ._addFileTypes<FileRouteTypes>() 339 + IndexRoute: IndexRoute, 340 + McpRoute: McpRoute, 341 + AlbumAlbumIdRoute: AlbumAlbumIdRoute, 342 + DemoMcpTodosRoute: DemoMcpTodosRoute, 343 + DemoTanstackQueryRoute: DemoTanstackQueryRoute, 344 + AlbumIndexRoute: AlbumIndexRoute, 345 + DemoApiMcpTodosRoute: DemoApiMcpTodosRoute, 346 + DemoApiNamesRoute: DemoApiNamesRoute, 347 + DemoApiTqTodosRoute: DemoApiTqTodosRoute, 348 + DemoStartApiRequestRoute: DemoStartApiRequestRoute, 349 + DemoStartServerFuncsRoute: DemoStartServerFuncsRoute, 350 + DemoStartSsrDataOnlyRoute: DemoStartSsrDataOnlyRoute, 351 + DemoStartSsrFullSsrRoute: DemoStartSsrFullSsrRoute, 352 + DemoStartSsrSpaModeRoute: DemoStartSsrSpaModeRoute, 353 + DemoStartSsrIndexRoute: DemoStartSsrIndexRoute, 354 + }; 355 + export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren)._addFileTypes<FileRouteTypes>(); 358 356 359 - import type { getRouter } from './router.tsx' 360 - import type { createStart } from '@tanstack/react-start' 361 - declare module '@tanstack/react-start' { 362 - interface Register { 363 - ssr: true 364 - router: Awaited<ReturnType<typeof getRouter>> 365 - } 357 + import type { getRouter } from "./router.tsx"; 358 + import type { createStart } from "@tanstack/react-start"; 359 + declare module "@tanstack/react-start" { 360 + interface Register { 361 + ssr: true; 362 + router: Awaited<ReturnType<typeof getRouter>>; 363 + } 366 364 }