From 5008ac565a298eca93d9d7f430e3f8c04f3674ce Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sat, 18 Jul 2026 05:56:23 +0000 Subject: [PATCH 1/5] chore: remove unused sample_data and its dangling commented-out import sample_data.ts was a leftover placeholder predating real DB-backed task/answer fetching; nothing referenced it besides a commented-out import. --- src/lib/components/TagListForEdit.svelte | 1 - src/lib/server/sample_data.ts | 48 ------------------------ 2 files changed, 49 deletions(-) delete mode 100644 src/lib/server/sample_data.ts diff --git a/src/lib/components/TagListForEdit.svelte b/src/lib/components/TagListForEdit.svelte index 49f4dc972..53bc907e3 100644 --- a/src/lib/components/TagListForEdit.svelte +++ b/src/lib/components/TagListForEdit.svelte @@ -14,7 +14,6 @@ //import { ATCODER_BASE_CONTEST_URL } from '$lib/constants/urls'; import { newline } from '$lib/utils/newline'; - //import { tasks } from '../server/sample_data'; //gradeでソート済みのTaskのリストと、APIから取得したtasklistを表示する //xport let tasks: Task[]; diff --git a/src/lib/server/sample_data.ts b/src/lib/server/sample_data.ts deleted file mode 100644 index c098cef0f..000000000 --- a/src/lib/server/sample_data.ts +++ /dev/null @@ -1,48 +0,0 @@ -// TODO: Enable to fetch data from the database via API. -export const tasks = [ - { - contest_id: 'abc318', - task_id: 'abc318_a', - title: 'A - foo', - grade: 'Q7', - }, - { - contest_id: 'abc231', - task_id: 'abc231_a', - title: 'A - Water Pressure', - grade: 'Q10', - }, - { - contest_id: 'abc214', - task_id: 'abc214_a', - title: 'A - New Generation ABC', - grade: 'Q10', - }, - { - contest_id: 'abc202', - task_id: 'abc202_a', - title: 'A - Three Dice', - grade: 'Q9', - }, -]; - -export const answers = [ - { - task_id: 'abc231_a', - user_id: 'hogehoge', - submission_status: 'wa', - status_id: '2', - }, - { - task_id: 'abc214_a', - user_id: 'hogehoge', - submission_status: 'ac', - status_id: '3', - }, - { - task_id: 'abc202_a', - user_id: 'hogehoge', - submission_status: 'ns', - status_id: '1', - }, -]; From c41feb74978a0ddb668f89b0d41817ff4aa20db8 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sat, 18 Jul 2026 06:10:34 +0000 Subject: [PATCH 2/5] refactor(auth): move auth server internals under features/auth, rename session guards module Consolidates src/lib/server/{auth,session,password,random}.ts into src/features/auth/server/ to keep auth-domain code colocated per the feature-scoped structure. Renames services/session.ts to session_guards.ts to reflect its actual responsibility (guard/redirect helpers) and disambiguate from the new server/session.ts. --- .claude/rules/auth.md | 12 ++++++------ src/{lib => features/auth}/server/auth.test.ts | 4 ++-- src/{lib => features/auth}/server/auth.ts | 2 +- .../auth}/server/password.test.ts | 0 src/{lib => features/auth}/server/password.ts | 0 .../auth}/server/random.test.ts | 0 src/{lib => features/auth}/server/random.ts | 0 .../auth}/server/session.test.ts | 0 src/{lib => features/auth}/server/session.ts | 2 +- .../{session.test.ts => session_guards.test.ts} | 2 +- .../services/{session.ts => session_guards.ts} | 0 src/routes/problems/[slug]/+page.server.ts | 2 +- src/routes/users/[username]/+page.server.ts | 3 +-- src/routes/users/edit/+page.server.ts | 3 +-- src/routes/workbooks/+page.server.ts | 17 ++++++++--------- src/routes/workbooks/[slug]/+page.server.ts | 4 ++-- src/routes/workbooks/create/+page.server.ts | 5 ++--- .../workbooks/edit/[slug]/+page.server.ts | 2 +- 18 files changed, 27 insertions(+), 31 deletions(-) rename src/{lib => features/auth}/server/auth.test.ts (97%) rename src/{lib => features/auth}/server/auth.ts (98%) rename src/{lib => features/auth}/server/password.test.ts (100%) rename src/{lib => features/auth}/server/password.ts (100%) rename src/{lib => features/auth}/server/random.test.ts (100%) rename src/{lib => features/auth}/server/random.ts (100%) rename src/{lib => features/auth}/server/session.test.ts (100%) rename src/{lib => features/auth}/server/session.ts (98%) rename src/features/auth/services/{session.test.ts => session_guards.test.ts} (99%) rename src/features/auth/services/{session.ts => session_guards.ts} (100%) diff --git a/.claude/rules/auth.md b/.claude/rules/auth.md index a287e753e..7bcac6d04 100644 --- a/.claude/rules/auth.md +++ b/.claude/rules/auth.md @@ -1,10 +1,10 @@ --- description: Authentication rules paths: - - 'src/lib/server/auth.ts' - 'src/routes/(auth)/**' - - 'src/features/auth/**' - 'src/routes/(admin)/**' + - 'src/features/auth/**' + - 'src/features/auth/server/**' - 'src/hooks.server.ts' --- @@ -31,11 +31,11 @@ Auth is a self-managed implementation (no external auth library), kept compatibl ## Key Files -- `src/lib/server/auth.ts`: `createAuthRequest` (request-scoped session handle) -- `src/lib/server/session.ts` / `src/lib/server/password.ts`: self-managed session + password crypto -- `src/features/auth/services/credentials.ts`: `registerUser` / `authenticateUser` - `src/hooks.server.ts`: Global request handler -- `src/features/auth/services/session.ts`: +- `src/features/auth/server/auth.ts`: `createAuthRequest` (request-scoped session handle) +- `src/features/auth/server/session.ts` / `src/features/auth/server/password.ts`: self-managed session + password crypto (`random.ts` provides salt/session-id generation) +- `src/features/auth/services/credentials.ts`: `registerUser` / `authenticateUser` +- `src/features/auth/services/session_guards.ts`: - `getLoggedInUser(locals, url?)` — returns logged-in user or redirects to `/login` - `ensureSessionOrRedirect(locals, url?)` — guard-only; redirects if no session - `src/features/auth/services/admin_access.ts`: diff --git a/src/lib/server/auth.test.ts b/src/features/auth/server/auth.test.ts similarity index 97% rename from src/lib/server/auth.test.ts rename to src/features/auth/server/auth.test.ts index 63394b690..768775131 100644 --- a/src/lib/server/auth.test.ts +++ b/src/features/auth/server/auth.test.ts @@ -6,13 +6,13 @@ import { Roles } from '@prisma/client'; // dev = false so `secure: !dev` resolves to true (the production-relevant cookie flag) vi.mock('$app/environment', () => ({ dev: false })); -vi.mock('$lib/server/session', () => ({ +vi.mock('./session', () => ({ SESSION_COOKIE_NAME: 'auth_session', validateSession: vi.fn(), })); import { createAuthRequest } from './auth'; -import { validateSession, SESSION_COOKIE_NAME } from '$lib/server/session'; +import { validateSession, SESSION_COOKIE_NAME } from './session'; const mockValidateSession = validateSession as unknown as ReturnType; diff --git a/src/lib/server/auth.ts b/src/features/auth/server/auth.ts similarity index 98% rename from src/lib/server/auth.ts rename to src/features/auth/server/auth.ts index 34053f0f2..b2e84047b 100644 --- a/src/lib/server/auth.ts +++ b/src/features/auth/server/auth.ts @@ -6,7 +6,7 @@ import { validateSession, type SessionCookieData, type ValidatedSession, -} from '$lib/server/session'; +} from './session'; export type AuthRequest = { validate: () => Promise; diff --git a/src/lib/server/password.test.ts b/src/features/auth/server/password.test.ts similarity index 100% rename from src/lib/server/password.test.ts rename to src/features/auth/server/password.test.ts diff --git a/src/lib/server/password.ts b/src/features/auth/server/password.ts similarity index 100% rename from src/lib/server/password.ts rename to src/features/auth/server/password.ts diff --git a/src/lib/server/random.test.ts b/src/features/auth/server/random.test.ts similarity index 100% rename from src/lib/server/random.test.ts rename to src/features/auth/server/random.test.ts diff --git a/src/lib/server/random.ts b/src/features/auth/server/random.ts similarity index 100% rename from src/lib/server/random.ts rename to src/features/auth/server/random.ts diff --git a/src/lib/server/session.test.ts b/src/features/auth/server/session.test.ts similarity index 100% rename from src/lib/server/session.test.ts rename to src/features/auth/server/session.test.ts diff --git a/src/lib/server/session.ts b/src/features/auth/server/session.ts similarity index 98% rename from src/lib/server/session.ts rename to src/features/auth/server/session.ts index d84b315c1..00eeb952f 100644 --- a/src/lib/server/session.ts +++ b/src/features/auth/server/session.ts @@ -2,7 +2,7 @@ import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; import type { Roles } from '@prisma/client'; import client from '$lib/server/database'; -import { generateRandomString } from '$lib/server/random'; +import { generateRandomString } from './random'; // lucia v2 defaults: active 24h, idle +14d const ACTIVE_PERIOD_MS = 1000 * 60 * 60 * 24; diff --git a/src/features/auth/services/session.test.ts b/src/features/auth/services/session_guards.test.ts similarity index 99% rename from src/features/auth/services/session.test.ts rename to src/features/auth/services/session_guards.test.ts index 7b754eeab..96bcbe23f 100644 --- a/src/features/auth/services/session.test.ts +++ b/src/features/auth/services/session_guards.test.ts @@ -16,7 +16,7 @@ afterEach(() => { vi.clearAllMocks(); }); -import { ensureSessionOrRedirect, getLoggedInUser } from './session'; +import { ensureSessionOrRedirect, getLoggedInUser } from './session_guards'; const createMockLocalsWithValidSession = (user = { id: 'test-user', name: 'Test User' }) => ({ diff --git a/src/features/auth/services/session.ts b/src/features/auth/services/session_guards.ts similarity index 100% rename from src/features/auth/services/session.ts rename to src/features/auth/services/session_guards.ts diff --git a/src/routes/problems/[slug]/+page.server.ts b/src/routes/problems/[slug]/+page.server.ts index 32e33e8a9..a529830ba 100644 --- a/src/routes/problems/[slug]/+page.server.ts +++ b/src/routes/problems/[slug]/+page.server.ts @@ -2,7 +2,7 @@ import { fail, type Actions, redirect } from '@sveltejs/kit'; import * as crud from '$lib/services/task_results'; import { getButtons } from '$lib/services/submission_status'; -import { getLoggedInUser } from '$features/auth/services/session'; +import { getLoggedInUser } from '$features/auth/services/session_guards'; import { BAD_REQUEST, TEMPORARY_REDIRECT } from '$lib/constants/http-response-status-codes'; diff --git a/src/routes/users/[username]/+page.server.ts b/src/routes/users/[username]/+page.server.ts index 76daebfbc..0078522c9 100644 --- a/src/routes/users/[username]/+page.server.ts +++ b/src/routes/users/[username]/+page.server.ts @@ -2,12 +2,11 @@ import * as userService from '$lib/services/users'; import * as taskResultService from '$lib/services/task_results'; +import { getLoggedInUser } from '$features/auth/services/session_guards'; import type { Roles } from '$lib/types/user'; import type { TaskResult } from '$lib/types/task'; -import { getLoggedInUser } from '$features/auth/services/session'; - export async function load({ locals, params, url }) { const loggedInUser = await getLoggedInUser(locals, url); diff --git a/src/routes/users/edit/+page.server.ts b/src/routes/users/edit/+page.server.ts index 39f884086..b01be801d 100644 --- a/src/routes/users/edit/+page.server.ts +++ b/src/routes/users/edit/+page.server.ts @@ -6,8 +6,7 @@ import type { Roles } from '$lib/types/user'; import * as userService from '$lib/services/users'; import * as verificationService from '$features/account/services/atcoder_verification'; - -import { getLoggedInUser } from '$features/auth/services/session'; +import { getLoggedInUser } from '$features/auth/services/session_guards'; import { BAD_REQUEST, diff --git a/src/routes/workbooks/+page.server.ts b/src/routes/workbooks/+page.server.ts index 7bf04c196..12b90c522 100644 --- a/src/routes/workbooks/+page.server.ts +++ b/src/routes/workbooks/+page.server.ts @@ -1,9 +1,15 @@ import { error, redirect } from '@sveltejs/kit'; import * as taskCrud from '$lib/services/tasks'; -import { buildTaskIdsFromWorkbooks } from '$features/workbooks/utils/workbooks'; import * as taskResultsCrud from '$lib/services/task_results'; +import { getLoggedInUser } from '$features/auth/services/session_guards'; import * as workBooksCrud from '$features/workbooks/services/workbooks'; +import { + getWorkbooksByPlacement, + getWorkBooksCreatedByUsers, + getAvailableSolutionCategories, + getSolutionCategoryMapByWorkbookId, +} from '$features/workbooks/services/workbooks'; import { Roles } from '$lib/types/user'; import type { TaskGrade, TaskResult } from '$lib/types/task'; @@ -19,15 +25,8 @@ import { type SolutionCategory, } from '$features/workbooks/types/workbook_placement'; -import { - getWorkbooksByPlacement, - getWorkBooksCreatedByUsers, - getAvailableSolutionCategories, - getSolutionCategoryMapByWorkbookId, -} from '$features/workbooks/services/workbooks'; - import { isAdmin, canDelete } from '$lib/utils/authorship'; -import { getLoggedInUser } from '$features/auth/services/session'; +import { buildTaskIdsFromWorkbooks } from '$features/workbooks/utils/workbooks'; import { parseWorkBookTab, parseWorkBookGrade, diff --git a/src/routes/workbooks/[slug]/+page.server.ts b/src/routes/workbooks/[slug]/+page.server.ts index 336ed9684..c32ba7c07 100644 --- a/src/routes/workbooks/[slug]/+page.server.ts +++ b/src/routes/workbooks/[slug]/+page.server.ts @@ -4,15 +4,15 @@ import { zod4 } from 'sveltekit-superforms/adapters'; import { Roles } from '$lib/types/user'; +import { getLoggedInUser } from '$features/auth/services/session_guards'; import * as taskResultsCrud from '$lib/services/task_results'; -import { getWorkbookWithAuthor } from '$features/workbooks/services/workbooks'; import * as action from '$lib/actions/update_task_result'; +import { getWorkbookWithAuthor } from '$features/workbooks/services/workbooks'; import { getVoteGradeStatisticsForTaskIds } from '$features/votes/services/vote_statistics'; import { voteAbsoluteGrade as voteAbsoluteGradeAction } from '$features/votes/actions/vote_actions'; import { voteAbsoluteGradeSchema } from '$features/votes/zod/schema'; import { isAdmin, canRead } from '$lib/utils/authorship'; -import { getLoggedInUser } from '$features/auth/services/session'; import { parseWorkBookId, parseWorkBookUrlSlug } from '$features/workbooks/utils/workbook'; import { BAD_REQUEST, FORBIDDEN, NOT_FOUND } from '$lib/constants/http-response-status-codes'; diff --git a/src/routes/workbooks/create/+page.server.ts b/src/routes/workbooks/create/+page.server.ts index d645fffa3..81f6d57ba 100644 --- a/src/routes/workbooks/create/+page.server.ts +++ b/src/routes/workbooks/create/+page.server.ts @@ -2,14 +2,13 @@ import { error, fail, redirect } from '@sveltejs/kit'; import { superValidate } from 'sveltekit-superforms/server'; import { zod4 } from 'sveltekit-superforms/adapters'; -import { workBookSchema } from '$features/workbooks/zod/schema'; - import * as tasksCrud from '$lib/services/tasks'; +import { ensureSessionOrRedirect, getLoggedInUser } from '$features/auth/services/session_guards'; import * as workBooksCrud from '$features/workbooks/services/workbooks'; +import { workBookSchema } from '$features/workbooks/zod/schema'; import { Roles } from '$lib/types/user'; -import { ensureSessionOrRedirect, getLoggedInUser } from '$features/auth/services/session'; import { BAD_REQUEST, FORBIDDEN, diff --git a/src/routes/workbooks/edit/[slug]/+page.server.ts b/src/routes/workbooks/edit/[slug]/+page.server.ts index a14bb9875..79065edea 100644 --- a/src/routes/workbooks/edit/[slug]/+page.server.ts +++ b/src/routes/workbooks/edit/[slug]/+page.server.ts @@ -6,10 +6,10 @@ import { Roles } from '$lib/types/user'; import { workBookSchema } from '$features/workbooks/zod/schema'; import * as tasksCrud from '$lib/services/tasks'; +import { getLoggedInUser } from '$features/auth/services/session_guards'; import * as workBooksCrud from '$features/workbooks/services/workbooks'; import { canEdit, isAdmin } from '$lib/utils/authorship'; -import { getLoggedInUser } from '$features/auth/services/session'; import { parseWorkBookId, parseWorkBookUrlSlug } from '$features/workbooks/utils/workbook'; import { From 9e3b059029bbb7a3eeb7066b730a3cce9db7bc8c Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sat, 18 Jul 2026 06:16:45 +0000 Subject: [PATCH 3/5] refactor(auth): update remaining import paths after auth server move, document server/ convention Completes the src/lib/server -> src/features/auth/server migration by fixing the remaining consumers (hooks, app.d.ts, seed, auth routes, credentials service). Also documents the feature-scoped server/ subdirectory convention in architecture.md. --- docs/guides/architecture.md | 17 ++++++++++++++--- prisma/seed.ts | 2 +- src/app.d.ts | 2 +- src/features/auth/services/credentials.test.ts | 4 ++-- src/features/auth/services/credentials.ts | 4 ++-- src/hooks.server.ts | 2 +- src/routes/(auth)/login/+page.server.ts | 2 +- src/routes/(auth)/logout/+page.server.ts | 2 +- src/routes/(auth)/signup/+page.server.ts | 2 +- 9 files changed, 24 insertions(+), 13 deletions(-) diff --git a/docs/guides/architecture.md b/docs/guides/architecture.md index 68f58054c..e78ccd92f 100644 --- a/docs/guides/architecture.md +++ b/docs/guides/architecture.md @@ -91,6 +91,17 @@ src/features/ - `detail/` — 詳細ページ用コンポーネント - `shared/` — feature 内で複数ページから使うコンポーネント +**`server/` サブディレクトリ規約:** + +feature 内のサーバ専用・非サービスコード(リクエストスコープのハンドル、cookie 書き込み、 +crypto ユーティリティなど、`services/` の「純粋な値/`null` を返す framework 非依存」規約に +収まらないもの)は `features/{feature}/server/` に置く(`votes/server/`・`workbooks/server/`・ +`auth/server/` が前例)。 + +- `$lib/server/` を出るため SvelteKit のクライアント import 禁止(ビルド時エラー)は**効かない**。 + 保護は規約ベース + 推移的依存(`$lib/server/database` の import や `node:crypto` 依存)に委ねる +- 2つ以上の feature で使うサーバコードは `src/lib/server/` に昇格させる(例: `database.ts`) + ### ルート固有の `_types/` / `_utils/` ディレクトリ SvelteKit のルートディレクトリ内で、そのページ専用の型やユーティリティを colocate するために `_types/` と `_utils/` を使う。アンダースコアプレフィックスにより SvelteKit のルーティング対象外となる。 @@ -147,9 +158,9 @@ src/lib/ ├── clients/ # 外部 API クライアント(AtCoder Problems, AOJ) ├── components/ # 共通 UI コンポーネント(GradeLabel, TaskGradeList, TaskList, FormWrapper 等) ├── constants/ # アプリ定数 -├── server/ # サーバー専用コード -│ ├── auth.ts -│ ├── database.ts +├── server/ # サーバー専用の共有インフラ +│ ├── database.ts # Prisma クライアント(14+ サービスが依存) +│ ├── tasks/ # cache.ts など複数 feature 共有のサーバ処理 │ └── services/ # 複数 feature で使うビジネスロジック ├── stores/ # 共通ストア(error_message 等) ├── types/ # 共通型定義 diff --git a/prisma/seed.ts b/prisma/seed.ts index bf1f7bea3..9574a68ce 100755 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -19,7 +19,7 @@ import { defineWorkBookFactory, } from './.fabbrica'; import PQueue from 'p-queue'; -import { hashPassword } from '../src/lib/server/password'; +import { hashPassword } from '../src/features/auth/server/password'; import { getTaskGrade } from '../src/lib/types/task'; import type { PlacementCreate } from '../src/features/workbooks/types/workbook_placement'; diff --git a/src/app.d.ts b/src/app.d.ts index 16eef8e48..ec0f9a009 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -7,7 +7,7 @@ declare global { namespace App { // interface Error {} interface Locals { - auth: import('$lib/server/auth').AuthRequest; + auth: import('$features/auth/server/auth').AuthRequest; user: { id: string; name: string; diff --git a/src/features/auth/services/credentials.test.ts b/src/features/auth/services/credentials.test.ts index ee91fb346..35e909937 100644 --- a/src/features/auth/services/credentials.test.ts +++ b/src/features/auth/services/credentials.test.ts @@ -15,13 +15,13 @@ vi.mock('$lib/server/database', () => ({ }, })); -vi.mock('$lib/server/password', () => ({ +vi.mock('../server/password', () => ({ hashPassword: vi.fn(), verifyPassword: vi.fn(), })); import db from '$lib/server/database'; -import { hashPassword, verifyPassword } from '$lib/server/password'; +import { hashPassword, verifyPassword } from '../server/password'; import { registerUser, authenticateUser } from './credentials'; const mockDb = db as unknown as { diff --git a/src/features/auth/services/credentials.ts b/src/features/auth/services/credentials.ts index c2d0a78bd..4d1022e1c 100644 --- a/src/features/auth/services/credentials.ts +++ b/src/features/auth/services/credentials.ts @@ -1,8 +1,8 @@ import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; import client from '$lib/server/database'; -import { hashPassword, verifyPassword } from '$lib/server/password'; -import { generateRandomString } from '$lib/server/random'; +import { hashPassword, verifyPassword } from '../server/password'; +import { generateRandomString } from '../server/random'; const USER_ID_LENGTH = 15; // lucia v2 createUser default diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 1f33289f2..53f489df7 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -3,7 +3,7 @@ // https://tech-blog.rakus.co.jp/entry/20230209/sveltekit import type { Handle } from '@sveltejs/kit'; -import { createAuthRequest } from '$lib/server/auth'; +import { createAuthRequest } from '$features/auth/server/auth'; import * as userService from '$lib/services/users'; diff --git a/src/routes/(auth)/login/+page.server.ts b/src/routes/(auth)/login/+page.server.ts index 0e6f15285..2aa11dec6 100644 --- a/src/routes/(auth)/login/+page.server.ts +++ b/src/routes/(auth)/login/+page.server.ts @@ -2,7 +2,7 @@ // See src/lib/utils/auth_forms.ts for the current form handling approach. import { fail, redirect } from '@sveltejs/kit'; -import { createSession } from '$lib/server/session'; +import { createSession } from '$features/auth/server/session'; import { authenticateUser } from '$features/auth/services/credentials'; import { initializeAuthForm, validateAuthFormWithFallback } from '$lib/utils/auth_forms'; diff --git a/src/routes/(auth)/logout/+page.server.ts b/src/routes/(auth)/logout/+page.server.ts index d16f20e37..f7802255f 100644 --- a/src/routes/(auth)/logout/+page.server.ts +++ b/src/routes/(auth)/logout/+page.server.ts @@ -1,6 +1,6 @@ import { fail, redirect } from '@sveltejs/kit'; -import { invalidateSession } from '$lib/server/session'; +import { invalidateSession } from '$features/auth/server/session'; import { SEE_OTHER, UNAUTHORIZED } from '$lib/constants/http-response-status-codes'; import { HOME_PAGE } from '$lib/constants/navbar-links'; diff --git a/src/routes/(auth)/signup/+page.server.ts b/src/routes/(auth)/signup/+page.server.ts index 22e757149..95db3feea 100644 --- a/src/routes/(auth)/signup/+page.server.ts +++ b/src/routes/(auth)/signup/+page.server.ts @@ -2,7 +2,7 @@ // See src/lib/utils/auth_forms.ts for the current form handling approach. import { fail, redirect } from '@sveltejs/kit'; -import { createSession } from '$lib/server/session'; +import { createSession } from '$features/auth/server/session'; import { registerUser } from '$features/auth/services/credentials'; import { initializeAuthForm, validateAuthFormWithFallback } from '$lib/utils/auth_forms'; From 96fb196ac3e2d37fab1b030b7c2388c57c04fd90 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sat, 18 Jul 2026 10:03:37 +0000 Subject: [PATCH 4/5] docs(dev-notes): add auth-feature-migration plan --- .../2026-07-18/auth-feature-migration/plan.md | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 docs/dev-notes/2026-07-18/auth-feature-migration/plan.md diff --git a/docs/dev-notes/2026-07-18/auth-feature-migration/plan.md b/docs/dev-notes/2026-07-18/auth-feature-migration/plan.md new file mode 100644 index 000000000..ba3e071a1 --- /dev/null +++ b/docs/dev-notes/2026-07-18/auth-feature-migration/plan.md @@ -0,0 +1,175 @@ +# auth モジュールの features/auth への移行 + sample_data 削除 + +## Overview + +PR #3864 で lucia v2 を自前 auth 実装に置換した。その実装のうち `src/lib/server/` に置かれた +`auth.ts` / `session.ts` / `password.ts` / `random.ts` は、importer 全数調査の結果 **auth ドメイン専用** +であることが確認された(hooks / (auth) ルート / `features/auth/services/credentials.ts` / `prisma/seed.ts` のみ)。 +`docs/guides/architecture.md` の3層モデル「単一ドメインのコード → `features/{feature}/`」に従い、 +`features/auth/server/` へ移行する(`votes/server/`・`workbooks/server/` の前例に準拠)。 + +`database.ts`(14+ サービスが依存する共有インフラ)と `tasks/cache.ts`(auth 無関係)は `lib/server/` に残す。 + +また `src/lib/server/sample_data.ts` はデッドコード確定(実参照ゼロ、唯一のヒットは +`TagListForEdit.svelte:17` のコメントアウト import)のため削除する。 + +## 設計根拠 + +- **移行先は `src/features/auth/server/`**: サーバ専用・非サービスコードの置き場として + `votes/server/`・`workbooks/server/` の前例に一致。既存 `services/session.ts` との名前衝突も回避 +- **ビルド時保護の実質維持**: `$lib/server/` を出ると SvelteKit のクライアント import 禁止は効かなくなるが、 + `session.ts`/`auth.ts` は `$lib/server/database` の推移的 import で保護が残り、 + `password.ts`/`random.ts` は `node:crypto`・サーバ文脈依存でクライアントビルド不可 +- **名前衝突の解消**: 既存 `features/auth/services/session.ts`(redirect ガード)を + `session_guards.ts` にリネームし、移行してくる `server/session.ts`(DB ライフサイクル)と区別 + +## 却下した代替案 + +1. **`services/session.ts` への統合**: redirect を throw するルートガードと framework 非依存の + DB ライフサイクルで責務が別物。テストのモック戦略も別系統(`vi.mock('@sveltejs/kit')` vs + `vi.mock('$lib/server/database')`)で、統合すると1ファイルに2系統のモックが同居する。 + さらに `server/auth.ts` → `validateSession` の依存が redirect/`buildLoginPath` を含む + ルート向けモジュールへ向かい、レイヤ方向が逆転する +2. **`auth.ts`/`password.ts`/`random.ts` を `features/auth/utils/` へ**: auth.ts は cookie 書き込み + (副作用)+ `RequestEvent` 依存で utils 規約「No side effects」違反。password.ts は `node:crypto` + 依存のサーバ専用 crypto で、クライアント importable な既存 utils(`buildLoginPath` 等)との同居は + 誤 import を誘発。random.ts のみ字面上は utils 適格(Web Crypto、副作用なし)だが、用途が + ソルト・セッション ID 生成でクライアント実行の正当なユースケースがなく、消費者もサーバ側3つのみ。 + 凝集性と YAGNI で server/ 同居とし、2ドメイン目の利用が現れた時点で `src/lib/utils/` へ抽出 +3. **`*.server.ts` サフィックスでビルド時保護を完全維持**: ファイル名規約(snake_case 単純名)から逸脱し、 + 既存 `votes/server/`・`workbooks/server/` の前例とも不揃い。推移的保護で実質十分と判断 +4. **移行しない(`lib/server/` 維持)**: architecture.md 自体が `lib/server/auth.ts` を明記しており + 現状も文書上は正当化可能だが、3層モデル「単一ドメイン → features」との矛盾が残る + +## 実装手順(低リスク → 高リスク) + +### Phase 1: sample_data.ts 削除(独立コミット) + +- [ ] `src/lib/server/sample_data.ts` を削除 +- [ ] `src/lib/components/TagListForEdit.svelte:17` のコメントアウト import 行を削除 +- [ ] Residual-Reference Sweep: `rg -ni 'sample_data' -g '*.ts' -g '*.svelte' -g '*.md'` + (生成物 `.svelte-kit/` `build/` は無視) + +**サマリ**: 三条件ルール(呼び出しゼロ・代替不要・依存フィールドなし)を満たすデッドコードの除去。 + +### Phase 2: services/session.ts → session_guards.ts リネーム(独立コミット) + +- [ ] `git mv` で `src/features/auth/services/session.ts` → `session_guards.ts`、 + `session.test.ts` → `session_guards.test.ts`(テスト内の相対 import `./session` → `./session_guards`) +- [ ] import 更新(7ルート、`'$features/auth/services/session'` → `'.../session_guards'`): + - [ ] `src/routes/problems/[slug]/+page.server.ts:5` + - [ ] `src/routes/users/edit/+page.server.ts:10` + - [ ] `src/routes/users/[username]/+page.server.ts:9` + - [ ] `src/routes/workbooks/+page.server.ts:30` + - [ ] `src/routes/workbooks/create/+page.server.ts:12` + - [ ] `src/routes/workbooks/edit/[slug]/+page.server.ts:12` + - [ ] `src/routes/workbooks/[slug]/+page.server.ts:15` +- [ ] `.claude/rules/auth.md` の Key Files 該当行を更新 +- [ ] `pnpm test:unit && pnpm check && pnpm lint` + +**サマリ**: 移行してくる `server/session.ts` との名前衝突を、責務を表す名前へのリネームで解消。 + +### Phase 3: auth 4モジュール移行(本体コミット) + +- [ ] `git mv` で 8 ファイルを `src/lib/server/` → `src/features/auth/server/` へ: + `auth.ts` / `auth.test.ts` / `session.ts` / `session.test.ts` / + `password.ts` / `password.test.ts` / `random.ts` / `random.test.ts` +- [ ] feature 内部の相互参照を相対 import に(既存慣例: `services/` の `../utils/login` 参照と同様): + - [ ] `server/session.ts`: `$lib/server/random` → `./random`(`$lib/server/database` は**そのまま**) + - [ ] `server/auth.ts`: session への import 指定子を確認し `./session` に + - [ ] `server/password.ts`: `./random` のまま(変更不要) +- [ ] 外部 importer の更新: + - [ ] `src/hooks.server.ts:6`: `'$lib/server/auth'` → `'$features/auth/server/auth'` + - [ ] `src/app.d.ts:10`: `import('$lib/server/auth').AuthRequest` → `import('$features/auth/server/auth').AuthRequest` + - [ ] `src/routes/(auth)/login/+page.server.ts:5`・`signup/+page.server.ts:5`(`createSession`)、 + `logout/+page.server.ts:3`(`invalidateSession`): `'$lib/server/session'` → `'$features/auth/server/session'` + - [ ] `src/features/auth/services/credentials.ts:4-5`: `'$lib/server/password'` / `'$lib/server/random'` + → 相対 `'../server/password'` / `'../server/random'` + - [ ] `prisma/seed.ts:22`: `'../src/lib/server/password'` → `'../src/features/auth/server/password'` +- [ ] テストのモック指定子を**ソースの import 指定子と一致させて**更新: + - [ ] `server/auth.test.ts`: `vi.mock('$lib/server/session')` → auth.ts の新指定子に一致(相対なら `'./session'`) + - [ ] `server/session.test.ts`: `vi.mock('$lib/server/database')` は**そのまま**、 + `$lib/server/random` のモックがあれば `'./random'` に + - [ ] `src/features/auth/services/credentials.test.ts:18,24`: `vi.mock('$lib/server/password')` + → credentials.ts の新指定子に一致 +- [ ] Residual-Reference Sweep(repo 全体): + `rg -n 'lib/server/(auth|session|password|random)' -g '*.ts' -g '*.svelte' -g '*.md'` + → ヒットは docs のみになるはず(Phase 4 で対応) +- [ ] `pnpm test:unit && pnpm check && pnpm lint && pnpm build` + +**サマリ**: auth ドメイン専用の4モジュール+テストを3層モデルに従い feature 内へ移動。 +`database.ts` / `tasks/cache.ts` は共有インフラとして `lib/server/` に残留。 + +### Phase 4: ドキュメント更新(Phase 3 と同コミットでも可) + +- [ ] `.claude/rules/auth.md` Key Files: `src/lib/server/auth.ts` / `session.ts` / `password.ts` + → `src/features/auth/server/...` に更新 +- [ ] `docs/guides/architecture.md`: `src/lib/server/` セクションから auth.ts の記載を除去し、 + 残留物(`database.ts` / `tasks/cache.ts`)を反映。`features/{feature}/server/` サブディレクトリ規約 + (サーバ専用・非サービスコード置き場)を明文化 + +**サマリ**: コードと文書の乖離を解消し、feature 内 `server/` 規約を明文化。 + +### Phase 5: リファクタサイクル(AGENTS.md 規約) + +- [x] `coderabbit review --plain` を実行し、critical / high / potential_issue 所見を本ファイルの + `## CodeRabbit Findings` に記録(**修正はユーザー判断待ち、独断で直さない**) +- [ ] novel lessons と残タスクを本ファイルに記録 + +## 検証 + +各 Phase 後: + +```bash +pnpm test:unit # 特に features/auth 配下と移行テスト 8 ファイル +pnpm check # app.d.ts の型 import 解決を確認 +pnpm lint +``` + +Phase 3 後は追加で: + +```bash +pnpm build # $lib/server 推移保護・alias 解決の最終確認 +``` + +E2E(auth フロー: login / signup / logout)を `pnpm test:e2e` で実行、 +または dev サーバで手動ログイン確認。 + +## 注意点 + +- `vi.mock()` の第1引数はソースの import 指定子と完全一致が必要 — 移行時の主要な破損ポイント +- `app.d.ts` は ambient d.ts だが `$features` alias は `.svelte-kit/tsconfig.json` の paths で解決される + (`pnpm check` で確認) +- `features/auth/server/` には SvelteKit のビルド時クライアント import 禁止は**効かない**(規約ベース)。 + 実質保護: session/auth は `$lib/server/database` 推移 import、password/random は node:crypto / サーバ文脈 +- `.svelte-kit/` `build/` 内の残留ヒットは生成物なので無視 + +## CodeRabbit Findings + +`coderabbit review --plain`(v0.6.5、`#3870 → staging`)を実行。48ファイル・21所見 +(major 17 / minor 4)。うち**本移行の変更スコープに関係するのは 4 件のみ**。残り 17 件は +ブランチに未追跡で含まれる無関係ファイル(`docs/ui-mock/*`・`docs/dev-notes/*`・ +`docs/superpowers/plans/*`)への指摘で、本タスクとは別物のため下記「スコープ外」に集約。 + +**修正はユーザー判断待ち。独断では直していない。** + +### スコープ内(auth 移行関連) + +| # | 重要度 | 箇所 | 指摘 | 評価 | +| ---- | ----------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CR-1 | major (Security) | `docs/guides/architecture.md:94-104` | `features/*/server/` に実効的なサーバー境界(`*.server.ts` / 保護配置 / CI 検査)を追加すべき。推移的依存に頼る現状では、将来 node:crypto・DB 非依存の補助コードが追加された時点でクライアントから import 可能になる | **却下した代替案 #3 + 注意点で既に明示的に受容したトレードオフ**。CodeRabbit は受容済みの設計判断を再提起している。CI 検査(eslint の import 制約等)の追加は将来の別タスク候補 | +| CR-2 | major (Correctness) | `src/features/auth/server/auth.ts:51-57` | `validate()` の有効セッション経路で Cookie を再発行しないため、セッション層がアイドル期限を延長してもブラウザは初回 `expires` 到達後に Cookie 送信を停止 → 利用中ユーザーが強制ログアウトされうる。更新有無と新 `idlePeriodExpiresAt` を返却契約に含め、更新時のみ `setSessionCookie()` を呼ぶべき | **PR #3864 由来の既存挙動**。本移行はファイル移動と import 指定子変更のみで、この挙動は不変更。正当な潜在バグだが移行スコープ外。別 issue 化を推奨 | +| CR-3 | minor (Correctness) | `src/features/auth/server/auth.test.ts:24-29` | Cookie モックが状態を持たず、`delete()` 後も `get()` が旧セッション ID を返す。実 SvelteKit では削除直後の同一リクエスト内 `get()` は `undefined`。ステートフルなモックにすべき | **既存テストを移動しただけ**。テスト品質改善。minor のため PR CI に委ねてよい | +| CR-4 | major (Maintainability) | `src/features/auth/services/session_guards.ts:24-48` | `getLoggedInUser` / `ensureSessionOrRedirect` が `redirect()` を直接呼び、サービス層をフレームワーク依存にしている。コーディング規約「Services return data or null; never call redirect()」違反。`server/` 等へ移動しルートから呼ぶべき | **既存挙動**(リネーム前から redirect を呼んでいた)。本タスクは衝突回避のリネームのみが目的で、責務再配置は却下した代替案 #1 の範囲。ただし規約違反の指摘自体は妥当 — 別タスクで `session_guards` を `server/` 直下 or route ハンドラへ寄せる検討余地あり | + +### スコープ外(未追跡の無関係ファイル、17 件) + +本移行と無関係。ブランチに未コミットで存在する別作業の成果物への指摘: + +- `docs/ui-mock/2026-{06-27,07-04,07-11}/index.html` — 10 件(投票ピッカーの `position:fixed` + スクロール座標バグ、その他 UI モックの機能不整合) +- `docs/dev-notes/{2026-04-26,2026-06-28,2026-07-01,2026-07-13}/...` — 5 件(ドキュメント記述の + 正確性: Remote Functions の種類数、PR マージ可否総括、Bun 移行 survey 等) +- `docs/superpowers/plans/2026-06-11-full-codebase-refactoring.md` — 3 件(計画文書の記述) + +→ 各成果物の担当タスクで対応すべきもの。本 PR には含めない。 From 2f6d4dc752d43079870f6a84f7d84bf5cea5884b Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sat, 18 Jul 2026 10:04:22 +0000 Subject: [PATCH 5/5] docs(dev-notes): remove auth-feature-migration plan --- .../2026-07-18/auth-feature-migration/plan.md | 175 ------------------ 1 file changed, 175 deletions(-) delete mode 100644 docs/dev-notes/2026-07-18/auth-feature-migration/plan.md diff --git a/docs/dev-notes/2026-07-18/auth-feature-migration/plan.md b/docs/dev-notes/2026-07-18/auth-feature-migration/plan.md deleted file mode 100644 index ba3e071a1..000000000 --- a/docs/dev-notes/2026-07-18/auth-feature-migration/plan.md +++ /dev/null @@ -1,175 +0,0 @@ -# auth モジュールの features/auth への移行 + sample_data 削除 - -## Overview - -PR #3864 で lucia v2 を自前 auth 実装に置換した。その実装のうち `src/lib/server/` に置かれた -`auth.ts` / `session.ts` / `password.ts` / `random.ts` は、importer 全数調査の結果 **auth ドメイン専用** -であることが確認された(hooks / (auth) ルート / `features/auth/services/credentials.ts` / `prisma/seed.ts` のみ)。 -`docs/guides/architecture.md` の3層モデル「単一ドメインのコード → `features/{feature}/`」に従い、 -`features/auth/server/` へ移行する(`votes/server/`・`workbooks/server/` の前例に準拠)。 - -`database.ts`(14+ サービスが依存する共有インフラ)と `tasks/cache.ts`(auth 無関係)は `lib/server/` に残す。 - -また `src/lib/server/sample_data.ts` はデッドコード確定(実参照ゼロ、唯一のヒットは -`TagListForEdit.svelte:17` のコメントアウト import)のため削除する。 - -## 設計根拠 - -- **移行先は `src/features/auth/server/`**: サーバ専用・非サービスコードの置き場として - `votes/server/`・`workbooks/server/` の前例に一致。既存 `services/session.ts` との名前衝突も回避 -- **ビルド時保護の実質維持**: `$lib/server/` を出ると SvelteKit のクライアント import 禁止は効かなくなるが、 - `session.ts`/`auth.ts` は `$lib/server/database` の推移的 import で保護が残り、 - `password.ts`/`random.ts` は `node:crypto`・サーバ文脈依存でクライアントビルド不可 -- **名前衝突の解消**: 既存 `features/auth/services/session.ts`(redirect ガード)を - `session_guards.ts` にリネームし、移行してくる `server/session.ts`(DB ライフサイクル)と区別 - -## 却下した代替案 - -1. **`services/session.ts` への統合**: redirect を throw するルートガードと framework 非依存の - DB ライフサイクルで責務が別物。テストのモック戦略も別系統(`vi.mock('@sveltejs/kit')` vs - `vi.mock('$lib/server/database')`)で、統合すると1ファイルに2系統のモックが同居する。 - さらに `server/auth.ts` → `validateSession` の依存が redirect/`buildLoginPath` を含む - ルート向けモジュールへ向かい、レイヤ方向が逆転する -2. **`auth.ts`/`password.ts`/`random.ts` を `features/auth/utils/` へ**: auth.ts は cookie 書き込み - (副作用)+ `RequestEvent` 依存で utils 規約「No side effects」違反。password.ts は `node:crypto` - 依存のサーバ専用 crypto で、クライアント importable な既存 utils(`buildLoginPath` 等)との同居は - 誤 import を誘発。random.ts のみ字面上は utils 適格(Web Crypto、副作用なし)だが、用途が - ソルト・セッション ID 生成でクライアント実行の正当なユースケースがなく、消費者もサーバ側3つのみ。 - 凝集性と YAGNI で server/ 同居とし、2ドメイン目の利用が現れた時点で `src/lib/utils/` へ抽出 -3. **`*.server.ts` サフィックスでビルド時保護を完全維持**: ファイル名規約(snake_case 単純名)から逸脱し、 - 既存 `votes/server/`・`workbooks/server/` の前例とも不揃い。推移的保護で実質十分と判断 -4. **移行しない(`lib/server/` 維持)**: architecture.md 自体が `lib/server/auth.ts` を明記しており - 現状も文書上は正当化可能だが、3層モデル「単一ドメイン → features」との矛盾が残る - -## 実装手順(低リスク → 高リスク) - -### Phase 1: sample_data.ts 削除(独立コミット) - -- [ ] `src/lib/server/sample_data.ts` を削除 -- [ ] `src/lib/components/TagListForEdit.svelte:17` のコメントアウト import 行を削除 -- [ ] Residual-Reference Sweep: `rg -ni 'sample_data' -g '*.ts' -g '*.svelte' -g '*.md'` - (生成物 `.svelte-kit/` `build/` は無視) - -**サマリ**: 三条件ルール(呼び出しゼロ・代替不要・依存フィールドなし)を満たすデッドコードの除去。 - -### Phase 2: services/session.ts → session_guards.ts リネーム(独立コミット) - -- [ ] `git mv` で `src/features/auth/services/session.ts` → `session_guards.ts`、 - `session.test.ts` → `session_guards.test.ts`(テスト内の相対 import `./session` → `./session_guards`) -- [ ] import 更新(7ルート、`'$features/auth/services/session'` → `'.../session_guards'`): - - [ ] `src/routes/problems/[slug]/+page.server.ts:5` - - [ ] `src/routes/users/edit/+page.server.ts:10` - - [ ] `src/routes/users/[username]/+page.server.ts:9` - - [ ] `src/routes/workbooks/+page.server.ts:30` - - [ ] `src/routes/workbooks/create/+page.server.ts:12` - - [ ] `src/routes/workbooks/edit/[slug]/+page.server.ts:12` - - [ ] `src/routes/workbooks/[slug]/+page.server.ts:15` -- [ ] `.claude/rules/auth.md` の Key Files 該当行を更新 -- [ ] `pnpm test:unit && pnpm check && pnpm lint` - -**サマリ**: 移行してくる `server/session.ts` との名前衝突を、責務を表す名前へのリネームで解消。 - -### Phase 3: auth 4モジュール移行(本体コミット) - -- [ ] `git mv` で 8 ファイルを `src/lib/server/` → `src/features/auth/server/` へ: - `auth.ts` / `auth.test.ts` / `session.ts` / `session.test.ts` / - `password.ts` / `password.test.ts` / `random.ts` / `random.test.ts` -- [ ] feature 内部の相互参照を相対 import に(既存慣例: `services/` の `../utils/login` 参照と同様): - - [ ] `server/session.ts`: `$lib/server/random` → `./random`(`$lib/server/database` は**そのまま**) - - [ ] `server/auth.ts`: session への import 指定子を確認し `./session` に - - [ ] `server/password.ts`: `./random` のまま(変更不要) -- [ ] 外部 importer の更新: - - [ ] `src/hooks.server.ts:6`: `'$lib/server/auth'` → `'$features/auth/server/auth'` - - [ ] `src/app.d.ts:10`: `import('$lib/server/auth').AuthRequest` → `import('$features/auth/server/auth').AuthRequest` - - [ ] `src/routes/(auth)/login/+page.server.ts:5`・`signup/+page.server.ts:5`(`createSession`)、 - `logout/+page.server.ts:3`(`invalidateSession`): `'$lib/server/session'` → `'$features/auth/server/session'` - - [ ] `src/features/auth/services/credentials.ts:4-5`: `'$lib/server/password'` / `'$lib/server/random'` - → 相対 `'../server/password'` / `'../server/random'` - - [ ] `prisma/seed.ts:22`: `'../src/lib/server/password'` → `'../src/features/auth/server/password'` -- [ ] テストのモック指定子を**ソースの import 指定子と一致させて**更新: - - [ ] `server/auth.test.ts`: `vi.mock('$lib/server/session')` → auth.ts の新指定子に一致(相対なら `'./session'`) - - [ ] `server/session.test.ts`: `vi.mock('$lib/server/database')` は**そのまま**、 - `$lib/server/random` のモックがあれば `'./random'` に - - [ ] `src/features/auth/services/credentials.test.ts:18,24`: `vi.mock('$lib/server/password')` - → credentials.ts の新指定子に一致 -- [ ] Residual-Reference Sweep(repo 全体): - `rg -n 'lib/server/(auth|session|password|random)' -g '*.ts' -g '*.svelte' -g '*.md'` - → ヒットは docs のみになるはず(Phase 4 で対応) -- [ ] `pnpm test:unit && pnpm check && pnpm lint && pnpm build` - -**サマリ**: auth ドメイン専用の4モジュール+テストを3層モデルに従い feature 内へ移動。 -`database.ts` / `tasks/cache.ts` は共有インフラとして `lib/server/` に残留。 - -### Phase 4: ドキュメント更新(Phase 3 と同コミットでも可) - -- [ ] `.claude/rules/auth.md` Key Files: `src/lib/server/auth.ts` / `session.ts` / `password.ts` - → `src/features/auth/server/...` に更新 -- [ ] `docs/guides/architecture.md`: `src/lib/server/` セクションから auth.ts の記載を除去し、 - 残留物(`database.ts` / `tasks/cache.ts`)を反映。`features/{feature}/server/` サブディレクトリ規約 - (サーバ専用・非サービスコード置き場)を明文化 - -**サマリ**: コードと文書の乖離を解消し、feature 内 `server/` 規約を明文化。 - -### Phase 5: リファクタサイクル(AGENTS.md 規約) - -- [x] `coderabbit review --plain` を実行し、critical / high / potential_issue 所見を本ファイルの - `## CodeRabbit Findings` に記録(**修正はユーザー判断待ち、独断で直さない**) -- [ ] novel lessons と残タスクを本ファイルに記録 - -## 検証 - -各 Phase 後: - -```bash -pnpm test:unit # 特に features/auth 配下と移行テスト 8 ファイル -pnpm check # app.d.ts の型 import 解決を確認 -pnpm lint -``` - -Phase 3 後は追加で: - -```bash -pnpm build # $lib/server 推移保護・alias 解決の最終確認 -``` - -E2E(auth フロー: login / signup / logout)を `pnpm test:e2e` で実行、 -または dev サーバで手動ログイン確認。 - -## 注意点 - -- `vi.mock()` の第1引数はソースの import 指定子と完全一致が必要 — 移行時の主要な破損ポイント -- `app.d.ts` は ambient d.ts だが `$features` alias は `.svelte-kit/tsconfig.json` の paths で解決される - (`pnpm check` で確認) -- `features/auth/server/` には SvelteKit のビルド時クライアント import 禁止は**効かない**(規約ベース)。 - 実質保護: session/auth は `$lib/server/database` 推移 import、password/random は node:crypto / サーバ文脈 -- `.svelte-kit/` `build/` 内の残留ヒットは生成物なので無視 - -## CodeRabbit Findings - -`coderabbit review --plain`(v0.6.5、`#3870 → staging`)を実行。48ファイル・21所見 -(major 17 / minor 4)。うち**本移行の変更スコープに関係するのは 4 件のみ**。残り 17 件は -ブランチに未追跡で含まれる無関係ファイル(`docs/ui-mock/*`・`docs/dev-notes/*`・ -`docs/superpowers/plans/*`)への指摘で、本タスクとは別物のため下記「スコープ外」に集約。 - -**修正はユーザー判断待ち。独断では直していない。** - -### スコープ内(auth 移行関連) - -| # | 重要度 | 箇所 | 指摘 | 評価 | -| ---- | ----------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| CR-1 | major (Security) | `docs/guides/architecture.md:94-104` | `features/*/server/` に実効的なサーバー境界(`*.server.ts` / 保護配置 / CI 検査)を追加すべき。推移的依存に頼る現状では、将来 node:crypto・DB 非依存の補助コードが追加された時点でクライアントから import 可能になる | **却下した代替案 #3 + 注意点で既に明示的に受容したトレードオフ**。CodeRabbit は受容済みの設計判断を再提起している。CI 検査(eslint の import 制約等)の追加は将来の別タスク候補 | -| CR-2 | major (Correctness) | `src/features/auth/server/auth.ts:51-57` | `validate()` の有効セッション経路で Cookie を再発行しないため、セッション層がアイドル期限を延長してもブラウザは初回 `expires` 到達後に Cookie 送信を停止 → 利用中ユーザーが強制ログアウトされうる。更新有無と新 `idlePeriodExpiresAt` を返却契約に含め、更新時のみ `setSessionCookie()` を呼ぶべき | **PR #3864 由来の既存挙動**。本移行はファイル移動と import 指定子変更のみで、この挙動は不変更。正当な潜在バグだが移行スコープ外。別 issue 化を推奨 | -| CR-3 | minor (Correctness) | `src/features/auth/server/auth.test.ts:24-29` | Cookie モックが状態を持たず、`delete()` 後も `get()` が旧セッション ID を返す。実 SvelteKit では削除直後の同一リクエスト内 `get()` は `undefined`。ステートフルなモックにすべき | **既存テストを移動しただけ**。テスト品質改善。minor のため PR CI に委ねてよい | -| CR-4 | major (Maintainability) | `src/features/auth/services/session_guards.ts:24-48` | `getLoggedInUser` / `ensureSessionOrRedirect` が `redirect()` を直接呼び、サービス層をフレームワーク依存にしている。コーディング規約「Services return data or null; never call redirect()」違反。`server/` 等へ移動しルートから呼ぶべき | **既存挙動**(リネーム前から redirect を呼んでいた)。本タスクは衝突回避のリネームのみが目的で、責務再配置は却下した代替案 #1 の範囲。ただし規約違反の指摘自体は妥当 — 別タスクで `session_guards` を `server/` 直下 or route ハンドラへ寄せる検討余地あり | - -### スコープ外(未追跡の無関係ファイル、17 件) - -本移行と無関係。ブランチに未コミットで存在する別作業の成果物への指摘: - -- `docs/ui-mock/2026-{06-27,07-04,07-11}/index.html` — 10 件(投票ピッカーの `position:fixed` - スクロール座標バグ、その他 UI モックの機能不整合) -- `docs/dev-notes/{2026-04-26,2026-06-28,2026-07-01,2026-07-13}/...` — 5 件(ドキュメント記述の - 正確性: Remote Functions の種類数、PR マージ可否総括、Bun 移行 survey 等) -- `docs/superpowers/plans/2026-06-11-full-codebase-refactoring.md` — 3 件(計画文書の記述) - -→ 各成果物の担当タスクで対応すべきもの。本 PR には含めない。