create-einja-app 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +307 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1041 -0
- package/dist/cli.js.map +1 -0
- package/package.json +62 -0
- package/templates/turborepo-pandacss/.biomeignore +15 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/biome-format.sh +49 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/design-doc-check.sh +61 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/detect-secrets.sh +62 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/large-file-warning.sh +42 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/playwright-resize.sh +36 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/typecheck.sh +37 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/unset-volta-recursion.sh +32 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/validate-git-commit.sh +239 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/warn-index-ts.sh +34 -0
- package/templates/turborepo-pandacss/.claude/hooks/einja/warn-relative-import.sh +48 -0
- package/templates/turborepo-pandacss/.claude/settings.json +174 -0
- package/templates/turborepo-pandacss/.claude/skills/create-einja-app-release/SKILL.md +186 -0
- package/templates/turborepo-pandacss/.claude/skills/dev-cli-release/SKILL.md +173 -0
- package/templates/turborepo-pandacss/.cursor/commands/spec-create.md +227 -0
- package/templates/turborepo-pandacss/.cursor/commands/start-dev.md +98 -0
- package/templates/turborepo-pandacss/.cursor/commands/task-exec.md +287 -0
- package/templates/turborepo-pandacss/.cursor/commands/task-vibe-kanban-loop.md +532 -0
- package/templates/turborepo-pandacss/.cursor/commands/update-docs-by-task-specs.md +448 -0
- package/templates/turborepo-pandacss/.cursor/mcp.json +45 -0
- package/templates/turborepo-pandacss/.cursor/rules/api-rules.mdc +171 -0
- package/templates/turborepo-pandacss/.cursor/rules/api-test-rules.mdc +181 -0
- package/templates/turborepo-pandacss/.cursor/rules/base-code.mdc +70 -0
- package/templates/turborepo-pandacss/.cursor/rules/base-commit-rules.mdc +174 -0
- package/templates/turborepo-pandacss/.cursor/rules/base-design.mdc +12 -0
- package/templates/turborepo-pandacss/.cursor/rules/base-rules.mdc +231 -0
- package/templates/turborepo-pandacss/.cursor/rules/error-handling-rules.mdc +188 -0
- package/templates/turborepo-pandacss/.cursor/rules/refactor-rules.mdc +93 -0
- package/templates/turborepo-pandacss/.dockerignore +126 -0
- package/templates/turborepo-pandacss/.einja-sync.json +35 -0
- package/templates/turborepo-pandacss/.env.ci +25 -0
- package/templates/turborepo-pandacss/.env.example +35 -0
- package/templates/turborepo-pandacss/.env.personal.example +27 -0
- package/templates/turborepo-pandacss/.envrc +4 -0
- package/templates/turborepo-pandacss/.gitattributes +5 -0
- package/templates/turborepo-pandacss/.husky/pre-commit +1 -0
- package/templates/turborepo-pandacss/.lintstagedrc.js +24 -0
- package/templates/turborepo-pandacss/.mcp.json +45 -0
- package/templates/turborepo-pandacss/.node-version +1 -0
- package/templates/turborepo-pandacss/.templateignore +60 -0
- package/templates/turborepo-pandacss/.vscode/extensions.json +3 -0
- package/templates/turborepo-pandacss/CLAUDE.md +415 -0
- package/templates/turborepo-pandacss/README.md +322 -0
- package/templates/turborepo-pandacss/apps/web/middleware.ts +28 -0
- package/templates/turborepo-pandacss/apps/web/next.config.ts +10 -0
- package/templates/turborepo-pandacss/apps/web/package.json +80 -0
- package/templates/turborepo-pandacss/apps/web/panda.config.ts +114 -0
- package/templates/turborepo-pandacss/apps/web/postcss.config.cjs +6 -0
- package/templates/turborepo-pandacss/apps/web/public/file.svg +1 -0
- package/templates/turborepo-pandacss/apps/web/public/globe.svg +1 -0
- package/templates/turborepo-pandacss/apps/web/public/next.svg +1 -0
- package/templates/turborepo-pandacss/apps/web/public/vercel.svg +1 -0
- package/templates/turborepo-pandacss/apps/web/public/window.svg +1 -0
- package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/dashboard/page.tsx +79 -0
- package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/data/_components/UserTable.tsx +203 -0
- package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/data/page.tsx +57 -0
- package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/layout-client.tsx +31 -0
- package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/layout.tsx +17 -0
- package/templates/turborepo-pandacss/apps/web/src/app/(authenticated)/profile/page.tsx +59 -0
- package/templates/turborepo-pandacss/apps/web/src/app/api/auth/[...nextauth]/route.ts +3 -0
- package/templates/turborepo-pandacss/apps/web/src/app/api/auth/signup/route.ts +70 -0
- package/templates/turborepo-pandacss/apps/web/src/app/error.tsx +106 -0
- package/templates/turborepo-pandacss/apps/web/src/app/favicon.ico +0 -0
- package/templates/turborepo-pandacss/apps/web/src/app/global-error.tsx +110 -0
- package/templates/turborepo-pandacss/apps/web/src/app/globals.css +121 -0
- package/templates/turborepo-pandacss/apps/web/src/app/layout.tsx +28 -0
- package/templates/turborepo-pandacss/apps/web/src/app/not-found.tsx +54 -0
- package/templates/turborepo-pandacss/apps/web/src/app/page.module.css +165 -0
- package/templates/turborepo-pandacss/apps/web/src/app/page.test.tsx +52 -0
- package/templates/turborepo-pandacss/apps/web/src/app/page.tsx +284 -0
- package/templates/turborepo-pandacss/apps/web/src/app/signin/page.tsx +296 -0
- package/templates/turborepo-pandacss/apps/web/src/app/signup/page.tsx +395 -0
- package/templates/turborepo-pandacss/apps/web/src/application/use-cases/UserUseCases.test.ts +229 -0
- package/templates/turborepo-pandacss/apps/web/src/application/use-cases/UserUseCases.ts +115 -0
- package/templates/turborepo-pandacss/apps/web/src/components/auth/login-button.tsx +35 -0
- package/templates/turborepo-pandacss/apps/web/src/components/auth/logout-button.tsx +24 -0
- package/templates/turborepo-pandacss/apps/web/src/components/auth/user-avatar.test.tsx +68 -0
- package/templates/turborepo-pandacss/apps/web/src/components/auth/user-avatar.tsx +43 -0
- package/templates/turborepo-pandacss/apps/web/src/components/dashboard/dashboard-stats.tsx +128 -0
- package/templates/turborepo-pandacss/apps/web/src/components/providers/query-provider.tsx +30 -0
- package/templates/turborepo-pandacss/apps/web/src/components/providers/session-provider.tsx +12 -0
- package/templates/turborepo-pandacss/apps/web/src/components/shared/Sidebar.tsx +175 -0
- package/templates/turborepo-pandacss/apps/web/src/components/shared/header.tsx +166 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/accordion.tsx +64 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/alert-dialog.tsx +135 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/alert.tsx +60 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/aspect-ratio.tsx +9 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/avatar.tsx +41 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/badge.tsx +39 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/breadcrumb.tsx +101 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/button.tsx +56 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/card.tsx +75 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/checkbox.tsx +29 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/data-table.tsx +189 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/dialog-hook.tsx +210 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/dialog.tsx +129 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/drawer.tsx +124 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/dropdown-menu.tsx +228 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/form.tsx +152 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/hover-card.tsx +38 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/input.tsx +21 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/label.tsx +21 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/pagination.tsx +105 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/popover.tsx +42 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/progress.tsx +28 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/select.tsx +170 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/separator.tsx +28 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/skeleton.tsx +13 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/sonner.tsx +25 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/table.tsx +92 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/tabs.tsx +54 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/textarea.tsx +18 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/tooltip.tsx +57 -0
- package/templates/turborepo-pandacss/apps/web/src/components/ui/typography.tsx +158 -0
- package/templates/turborepo-pandacss/apps/web/src/lib/auth/guard.ts +36 -0
- package/templates/turborepo-pandacss/apps/web/src/lib/auth/index.ts +22 -0
- package/templates/turborepo-pandacss/apps/web/src/lib/prisma.ts +3 -0
- package/templates/turborepo-pandacss/apps/web/src/lib/utils.ts +6 -0
- package/templates/turborepo-pandacss/apps/web/test/globals.d.ts +1 -0
- package/templates/turborepo-pandacss/apps/web/test/matchers.d.ts +1 -0
- package/templates/turborepo-pandacss/apps/web/test/setup.ts +22 -0
- package/templates/turborepo-pandacss/apps/web/tsconfig.json +37 -0
- package/templates/turborepo-pandacss/apps/web/vitest.config.ts +20 -0
- package/templates/turborepo-pandacss/apps/web/vitest.d.ts +2 -0
- package/templates/turborepo-pandacss/biome.json +60 -0
- package/templates/turborepo-pandacss/components.json +21 -0
- package/templates/turborepo-pandacss/docker-compose.yml +27 -0
- package/templates/turborepo-pandacss/middleware.ts +32 -0
- package/templates/turborepo-pandacss/next.config.ts +10 -0
- package/templates/turborepo-pandacss/package-lock.json +9346 -0
- package/templates/turborepo-pandacss/package.json +64 -0
- package/templates/turborepo-pandacss/packages/config/package.json +41 -0
- package/templates/turborepo-pandacss/packages/config/panda.config.ts +114 -0
- package/templates/turborepo-pandacss/packages/config/src/index.ts +24 -0
- package/templates/turborepo-pandacss/packages/config/src/worktree-config-loader.ts +129 -0
- package/templates/turborepo-pandacss/packages/config/src/worktree-config.ts +75 -0
- package/templates/turborepo-pandacss/packages/config/tsconfig.build.json +19 -0
- package/templates/turborepo-pandacss/packages/config/tsconfig.json +24 -0
- package/templates/turborepo-pandacss/packages/config/typescript/base.json +19 -0
- package/templates/turborepo-pandacss/packages/front-core/package.json +24 -0
- package/templates/turborepo-pandacss/packages/front-core/src/auth/config.ts +84 -0
- package/templates/turborepo-pandacss/packages/front-core/src/auth/index.ts +5 -0
- package/templates/turborepo-pandacss/packages/front-core/src/auth/types/next-auth.d.ts +20 -0
- package/templates/turborepo-pandacss/packages/front-core/src/auth/utils.ts +29 -0
- package/templates/turborepo-pandacss/packages/front-core/src/context/index.ts +2 -0
- package/templates/turborepo-pandacss/packages/front-core/src/hooks/index.ts +2 -0
- package/templates/turborepo-pandacss/packages/front-core/src/index.ts +4 -0
- package/templates/turborepo-pandacss/packages/front-core/src/utils/index.ts +2 -0
- package/templates/turborepo-pandacss/packages/front-core/tsconfig.json +14 -0
- package/templates/turborepo-pandacss/packages/server-core/package.json +32 -0
- package/templates/turborepo-pandacss/packages/server-core/prisma/schema.prisma +102 -0
- package/templates/turborepo-pandacss/packages/server-core/prisma/seed.ts +67 -0
- package/templates/turborepo-pandacss/packages/server-core/prisma.config.ts +8 -0
- package/templates/turborepo-pandacss/packages/server-core/src/__generated__/fabbrica/index.d.ts +270 -0
- package/templates/turborepo-pandacss/packages/server-core/src/__generated__/fabbrica/index.js +484 -0
- package/templates/turborepo-pandacss/packages/server-core/src/core/result.test.ts +78 -0
- package/templates/turborepo-pandacss/packages/server-core/src/core/result.ts +53 -0
- package/templates/turborepo-pandacss/packages/server-core/src/domain/.gitkeep +0 -0
- package/templates/turborepo-pandacss/packages/server-core/src/domain/entities/User.test.ts +232 -0
- package/templates/turborepo-pandacss/packages/server-core/src/domain/entities/User.ts +105 -0
- package/templates/turborepo-pandacss/packages/server-core/src/domain/repository-interfaces/IUserRepository.ts +101 -0
- package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/client.ts +15 -0
- package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/mappers/UserMapper.test.ts +278 -0
- package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/mappers/UserMapper.ts +103 -0
- package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/repositories/UserRepository.test.ts +317 -0
- package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/repositories/UserRepository.ts +169 -0
- package/templates/turborepo-pandacss/packages/server-core/src/testing/factories/index.ts +22 -0
- package/templates/turborepo-pandacss/packages/server-core/src/testing/factories/user.factory.ts +123 -0
- package/templates/turborepo-pandacss/packages/server-core/src/testing/fixtures/users.ts +92 -0
- package/templates/turborepo-pandacss/packages/server-core/src/testing/helpers/date.ts +49 -0
- package/templates/turborepo-pandacss/packages/server-core/src/testing/helpers/password.ts +50 -0
- package/templates/turborepo-pandacss/packages/server-core/src/testing/index.ts +26 -0
- package/templates/turborepo-pandacss/packages/server-core/tsconfig.json +14 -0
- package/templates/turborepo-pandacss/packages/server-core/vitest.config.ts +15 -0
- package/templates/turborepo-pandacss/packages/ui/package.json +37 -0
- package/templates/turborepo-pandacss/packages/ui/src/accordion.tsx +66 -0
- package/templates/turborepo-pandacss/packages/ui/src/alert-dialog.tsx +157 -0
- package/templates/turborepo-pandacss/packages/ui/src/alert.tsx +66 -0
- package/templates/turborepo-pandacss/packages/ui/src/aspect-ratio.tsx +11 -0
- package/templates/turborepo-pandacss/packages/ui/src/avatar.tsx +53 -0
- package/templates/turborepo-pandacss/packages/ui/src/badge.tsx +46 -0
- package/templates/turborepo-pandacss/packages/ui/src/breadcrumb.tsx +108 -0
- package/templates/turborepo-pandacss/packages/ui/src/button.tsx +59 -0
- package/templates/turborepo-pandacss/packages/ui/src/card.tsx +92 -0
- package/templates/turborepo-pandacss/packages/ui/src/checkbox.tsx +32 -0
- package/templates/turborepo-pandacss/packages/ui/src/data-table.tsx +216 -0
- package/templates/turborepo-pandacss/packages/ui/src/dialog-hook.tsx +226 -0
- package/templates/turborepo-pandacss/packages/ui/src/dialog.tsx +143 -0
- package/templates/turborepo-pandacss/packages/ui/src/drawer.tsx +135 -0
- package/templates/turborepo-pandacss/packages/ui/src/dropdown-menu.tsx +257 -0
- package/templates/turborepo-pandacss/packages/ui/src/form.tsx +168 -0
- package/templates/turborepo-pandacss/packages/ui/src/hover-card.tsx +44 -0
- package/templates/turborepo-pandacss/packages/ui/src/input.tsx +21 -0
- package/templates/turborepo-pandacss/packages/ui/src/label.tsx +24 -0
- package/templates/turborepo-pandacss/packages/ui/src/lib/utils.ts +6 -0
- package/templates/turborepo-pandacss/packages/ui/src/pagination.tsx +126 -0
- package/templates/turborepo-pandacss/packages/ui/src/popover.tsx +48 -0
- package/templates/turborepo-pandacss/packages/ui/src/progress.tsx +31 -0
- package/templates/turborepo-pandacss/packages/ui/src/select.tsx +185 -0
- package/templates/turborepo-pandacss/packages/ui/src/separator.tsx +28 -0
- package/templates/turborepo-pandacss/packages/ui/src/skeleton.tsx +13 -0
- package/templates/turborepo-pandacss/packages/ui/src/sonner.tsx +25 -0
- package/templates/turborepo-pandacss/packages/ui/src/table.tsx +116 -0
- package/templates/turborepo-pandacss/packages/ui/src/tabs.tsx +66 -0
- package/templates/turborepo-pandacss/packages/ui/src/textarea.tsx +18 -0
- package/templates/turborepo-pandacss/packages/ui/src/tooltip.tsx +61 -0
- package/templates/turborepo-pandacss/packages/ui/src/typography.tsx +187 -0
- package/templates/turborepo-pandacss/packages/ui/tsconfig.json +20 -0
- package/templates/turborepo-pandacss/panda.config.ts +114 -0
- package/templates/turborepo-pandacss/pnpm-lock.yaml +9032 -0
- package/templates/turborepo-pandacss/pnpm-workspace.yaml +11 -0
- package/templates/turborepo-pandacss/postcss.config.cjs +6 -0
- package/templates/turborepo-pandacss/prisma/schema.prisma +82 -0
- package/templates/turborepo-pandacss/public/file.svg +1 -0
- package/templates/turborepo-pandacss/public/globe.svg +1 -0
- package/templates/turborepo-pandacss/public/next.svg +1 -0
- package/templates/turborepo-pandacss/public/vercel.svg +1 -0
- package/templates/turborepo-pandacss/public/window.svg +1 -0
- package/templates/turborepo-pandacss/scripts/cli-template-update.ts +387 -0
- package/templates/turborepo-pandacss/scripts/env-show.ts +129 -0
- package/templates/turborepo-pandacss/scripts/env.ts +555 -0
- package/templates/turborepo-pandacss/scripts/init.sh +92 -0
- package/templates/turborepo-pandacss/scripts/setup-dev.ts +640 -0
- package/templates/turborepo-pandacss/scripts/template-update.ts +277 -0
- package/templates/turborepo-pandacss/scripts/worktree/dev.ts +872 -0
- package/templates/turborepo-pandacss/test/globals.d.ts +1 -0
- package/templates/turborepo-pandacss/test/setup.ts +22 -0
- package/templates/turborepo-pandacss/tsconfig.json +46 -0
- package/templates/turborepo-pandacss/turbo.json +57 -0
- package/templates/turborepo-pandacss/vitest.config.ts +20 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
import { UserFactory, initialize } from "../../testing";
|
|
3
|
+
import { User, type UserProps } from "./User";
|
|
4
|
+
|
|
5
|
+
describe("User Entity", () => {
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
// ドメインエンティティのテストではPrismaクライアントは使用しないため、空のオブジェクトを渡す
|
|
8
|
+
// biome-ignore lint/suspicious/noExplicitAny: test fixture initialization
|
|
9
|
+
initialize({ prisma: {} as any });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("constructor", () => {
|
|
13
|
+
it("有効なプロパティでエンティティが生成される", async () => {
|
|
14
|
+
// Given
|
|
15
|
+
const props = await UserFactory.build();
|
|
16
|
+
|
|
17
|
+
// When
|
|
18
|
+
const user = new User(props);
|
|
19
|
+
|
|
20
|
+
// Then
|
|
21
|
+
expect(user.id).toBeDefined();
|
|
22
|
+
expect(user.email).toBeDefined();
|
|
23
|
+
expect(user.name).toBeDefined();
|
|
24
|
+
expect(user.status).toBeDefined();
|
|
25
|
+
expect(user.role).toBeDefined();
|
|
26
|
+
expect(user.createdAt).toBeInstanceOf(Date);
|
|
27
|
+
expect(user.lastLogin).toBeInstanceOf(Date);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("nameがnullでもエンティティが生成される", async () => {
|
|
31
|
+
// Given
|
|
32
|
+
const props = await UserFactory.build({ name: null });
|
|
33
|
+
|
|
34
|
+
// When
|
|
35
|
+
const user = new User(props);
|
|
36
|
+
|
|
37
|
+
// Then
|
|
38
|
+
expect(user.name).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("lastLoginがnullでもエンティティが生成される", async () => {
|
|
42
|
+
// Given
|
|
43
|
+
const props = await UserFactory.build({ lastLogin: null });
|
|
44
|
+
|
|
45
|
+
// When
|
|
46
|
+
const user = new User(props);
|
|
47
|
+
|
|
48
|
+
// Then
|
|
49
|
+
expect(user.lastLogin).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("全てのstatus値でエンティティが生成される", async () => {
|
|
53
|
+
// Given & When & Then
|
|
54
|
+
const statuses = ["active", "inactive", "pending"] as const;
|
|
55
|
+
for (const status of statuses) {
|
|
56
|
+
const props = await UserFactory.build({ status });
|
|
57
|
+
const user = new User(props);
|
|
58
|
+
expect(user.status).toBe(status);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("全てのrole値でエンティティが生成される", async () => {
|
|
63
|
+
// Given & When & Then
|
|
64
|
+
const roles = ["admin", "user", "moderator"] as const;
|
|
65
|
+
for (const role of roles) {
|
|
66
|
+
const props = await UserFactory.build({ role });
|
|
67
|
+
const user = new User(props);
|
|
68
|
+
expect(user.role).toBe(role);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("withLastLogin", () => {
|
|
74
|
+
it("最終ログイン日時を更新した新しいインスタンスを返す", async () => {
|
|
75
|
+
// Given
|
|
76
|
+
const props = await UserFactory.build({ lastLogin: null });
|
|
77
|
+
const originalUser = new User(props);
|
|
78
|
+
const newLoginTime = new Date("2025-01-03T12:00:00Z");
|
|
79
|
+
|
|
80
|
+
// When
|
|
81
|
+
const updatedUser = originalUser.withLastLogin(newLoginTime);
|
|
82
|
+
|
|
83
|
+
// Then
|
|
84
|
+
expect(updatedUser.lastLogin).toEqual(newLoginTime);
|
|
85
|
+
expect(updatedUser).not.toBe(originalUser);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("元のインスタンスは変更されない(イミュータビリティ)", async () => {
|
|
89
|
+
// Given
|
|
90
|
+
const originalLastLogin = new Date("2025-01-02T00:00:00Z");
|
|
91
|
+
const props = await UserFactory.build({ lastLogin: originalLastLogin });
|
|
92
|
+
const originalUser = new User(props);
|
|
93
|
+
const newLoginTime = new Date("2025-01-03T12:00:00Z");
|
|
94
|
+
|
|
95
|
+
// When
|
|
96
|
+
originalUser.withLastLogin(newLoginTime);
|
|
97
|
+
|
|
98
|
+
// Then
|
|
99
|
+
expect(originalUser.lastLogin).toEqual(originalLastLogin);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("他のプロパティは維持される", async () => {
|
|
103
|
+
// Given
|
|
104
|
+
const props = await UserFactory.build();
|
|
105
|
+
const originalUser = new User(props);
|
|
106
|
+
const newLoginTime = new Date("2025-01-03T12:00:00Z");
|
|
107
|
+
|
|
108
|
+
// When
|
|
109
|
+
const updatedUser = originalUser.withLastLogin(newLoginTime);
|
|
110
|
+
|
|
111
|
+
// Then
|
|
112
|
+
expect(updatedUser.id).toBe(originalUser.id);
|
|
113
|
+
expect(updatedUser.email).toBe(originalUser.email);
|
|
114
|
+
expect(updatedUser.name).toBe(originalUser.name);
|
|
115
|
+
expect(updatedUser.status).toBe(originalUser.status);
|
|
116
|
+
expect(updatedUser.role).toBe(originalUser.role);
|
|
117
|
+
expect(updatedUser.createdAt).toEqual(originalUser.createdAt);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("withStatus", () => {
|
|
122
|
+
it("ステータスを更新した新しいインスタンスを返す", async () => {
|
|
123
|
+
// Given
|
|
124
|
+
const props = await UserFactory.build({ status: "pending" });
|
|
125
|
+
const originalUser = new User(props);
|
|
126
|
+
|
|
127
|
+
// When
|
|
128
|
+
const updatedUser = originalUser.withStatus("active");
|
|
129
|
+
|
|
130
|
+
// Then
|
|
131
|
+
expect(updatedUser.status).toBe("active");
|
|
132
|
+
expect(updatedUser).not.toBe(originalUser);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("元のインスタンスは変更されない(イミュータビリティ)", async () => {
|
|
136
|
+
// Given
|
|
137
|
+
const props = await UserFactory.build({ status: "pending" });
|
|
138
|
+
const originalUser = new User(props);
|
|
139
|
+
|
|
140
|
+
// When
|
|
141
|
+
originalUser.withStatus("active");
|
|
142
|
+
|
|
143
|
+
// Then
|
|
144
|
+
expect(originalUser.status).toBe("pending");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("withRole", () => {
|
|
149
|
+
it("ロールを更新した新しいインスタンスを返す", async () => {
|
|
150
|
+
// Given
|
|
151
|
+
const props = await UserFactory.build({ role: "user" });
|
|
152
|
+
const originalUser = new User(props);
|
|
153
|
+
|
|
154
|
+
// When
|
|
155
|
+
const updatedUser = originalUser.withRole("admin");
|
|
156
|
+
|
|
157
|
+
// Then
|
|
158
|
+
expect(updatedUser.role).toBe("admin");
|
|
159
|
+
expect(updatedUser).not.toBe(originalUser);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("元のインスタンスは変更されない(イミュータビリティ)", async () => {
|
|
163
|
+
// Given
|
|
164
|
+
const props = await UserFactory.build({ role: "user" });
|
|
165
|
+
const originalUser = new User(props);
|
|
166
|
+
|
|
167
|
+
// When
|
|
168
|
+
originalUser.withRole("admin");
|
|
169
|
+
|
|
170
|
+
// Then
|
|
171
|
+
expect(originalUser.role).toBe("user");
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("isActive", () => {
|
|
176
|
+
it("statusがactiveの場合、trueを返す", async () => {
|
|
177
|
+
// Given
|
|
178
|
+
const props = await UserFactory.build({ status: "active" });
|
|
179
|
+
const user = new User(props);
|
|
180
|
+
|
|
181
|
+
// When & Then
|
|
182
|
+
expect(user.isActive()).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("statusがinactiveの場合、falseを返す", async () => {
|
|
186
|
+
// Given
|
|
187
|
+
const props = await UserFactory.build({ status: "inactive" });
|
|
188
|
+
const user = new User(props);
|
|
189
|
+
|
|
190
|
+
// When & Then
|
|
191
|
+
expect(user.isActive()).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("statusがpendingの場合、falseを返す", async () => {
|
|
195
|
+
// Given
|
|
196
|
+
const props = await UserFactory.build({ status: "pending" });
|
|
197
|
+
const user = new User(props);
|
|
198
|
+
|
|
199
|
+
// When & Then
|
|
200
|
+
expect(user.isActive()).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("isAdmin", () => {
|
|
205
|
+
it("roleがadminの場合、trueを返す", async () => {
|
|
206
|
+
// Given
|
|
207
|
+
const props = await UserFactory.build({ role: "admin" });
|
|
208
|
+
const user = new User(props);
|
|
209
|
+
|
|
210
|
+
// When & Then
|
|
211
|
+
expect(user.isAdmin()).toBe(true);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("roleがuserの場合、falseを返す", async () => {
|
|
215
|
+
// Given
|
|
216
|
+
const props = await UserFactory.build({ role: "user" });
|
|
217
|
+
const user = new User(props);
|
|
218
|
+
|
|
219
|
+
// When & Then
|
|
220
|
+
expect(user.isAdmin()).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("roleがmoderatorの場合、falseを返す", async () => {
|
|
224
|
+
// Given
|
|
225
|
+
const props = await UserFactory.build({ role: "moderator" });
|
|
226
|
+
const user = new User(props);
|
|
227
|
+
|
|
228
|
+
// When & Then
|
|
229
|
+
expect(user.isAdmin()).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Entity
|
|
3
|
+
*
|
|
4
|
+
* ドメイン層のユーザーエンティティ。
|
|
5
|
+
* Prismaモデルから独立したドメインオブジェクトとして定義。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type UserStatus = "active" | "inactive" | "pending";
|
|
9
|
+
|
|
10
|
+
export type UserRole = "admin" | "user" | "moderator";
|
|
11
|
+
|
|
12
|
+
export interface UserProps {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly email: string;
|
|
15
|
+
readonly name: string | null;
|
|
16
|
+
readonly status: UserStatus;
|
|
17
|
+
readonly role: UserRole;
|
|
18
|
+
readonly createdAt: Date;
|
|
19
|
+
readonly lastLogin: Date | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ユーザーエンティティ
|
|
24
|
+
*
|
|
25
|
+
* イミュータブルなドメインオブジェクト。
|
|
26
|
+
* 状態変更は新しいインスタンスを生成することで行う。
|
|
27
|
+
*/
|
|
28
|
+
export class User {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly email: string;
|
|
31
|
+
readonly name: string | null;
|
|
32
|
+
readonly status: UserStatus;
|
|
33
|
+
readonly role: UserRole;
|
|
34
|
+
readonly createdAt: Date;
|
|
35
|
+
readonly lastLogin: Date | null;
|
|
36
|
+
|
|
37
|
+
constructor(props: UserProps) {
|
|
38
|
+
this.id = props.id;
|
|
39
|
+
this.email = props.email;
|
|
40
|
+
this.name = props.name;
|
|
41
|
+
this.status = props.status;
|
|
42
|
+
this.role = props.role;
|
|
43
|
+
this.createdAt = props.createdAt;
|
|
44
|
+
this.lastLogin = props.lastLogin;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 最終ログイン日時を更新した新しいUserインスタンスを返す
|
|
49
|
+
*/
|
|
50
|
+
withLastLogin(loginTime: Date): User {
|
|
51
|
+
return new User({
|
|
52
|
+
id: this.id,
|
|
53
|
+
email: this.email,
|
|
54
|
+
name: this.name,
|
|
55
|
+
status: this.status,
|
|
56
|
+
role: this.role,
|
|
57
|
+
createdAt: this.createdAt,
|
|
58
|
+
lastLogin: loginTime,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* ステータスを更新した新しいUserインスタンスを返す
|
|
64
|
+
*/
|
|
65
|
+
withStatus(status: UserStatus): User {
|
|
66
|
+
return new User({
|
|
67
|
+
id: this.id,
|
|
68
|
+
email: this.email,
|
|
69
|
+
name: this.name,
|
|
70
|
+
status,
|
|
71
|
+
role: this.role,
|
|
72
|
+
createdAt: this.createdAt,
|
|
73
|
+
lastLogin: this.lastLogin,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* ロールを更新した新しいUserインスタンスを返す
|
|
79
|
+
*/
|
|
80
|
+
withRole(role: UserRole): User {
|
|
81
|
+
return new User({
|
|
82
|
+
id: this.id,
|
|
83
|
+
email: this.email,
|
|
84
|
+
name: this.name,
|
|
85
|
+
status: this.status,
|
|
86
|
+
role,
|
|
87
|
+
createdAt: this.createdAt,
|
|
88
|
+
lastLogin: this.lastLogin,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* アクティブなユーザーかどうか
|
|
94
|
+
*/
|
|
95
|
+
isActive(): boolean {
|
|
96
|
+
return this.status === "active";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 管理者かどうか
|
|
101
|
+
*/
|
|
102
|
+
isAdmin(): boolean {
|
|
103
|
+
return this.role === "admin";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IUserRepository
|
|
3
|
+
*
|
|
4
|
+
* ユーザーリポジトリのインターフェース。
|
|
5
|
+
* Domain層で定義し、Infrastructure層で実装する。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Result } from "../../core/result";
|
|
9
|
+
import type { User, UserRole, UserStatus } from "../entities/User";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ユーザー検索条件
|
|
13
|
+
*/
|
|
14
|
+
export interface UserSearchCriteria {
|
|
15
|
+
/** ユーザーID */
|
|
16
|
+
readonly id?: string;
|
|
17
|
+
/** メールアドレス */
|
|
18
|
+
readonly email?: string;
|
|
19
|
+
/** ステータス */
|
|
20
|
+
readonly status?: UserStatus;
|
|
21
|
+
/** ロール */
|
|
22
|
+
readonly role?: UserRole;
|
|
23
|
+
/** 検索テキスト(名前やメールアドレスに対する部分一致) */
|
|
24
|
+
readonly search?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* ページネーションオプション
|
|
29
|
+
*/
|
|
30
|
+
export interface PaginationOptions {
|
|
31
|
+
/** ページ番号(1始まり) */
|
|
32
|
+
readonly page?: number;
|
|
33
|
+
/** 1ページあたりの件数 */
|
|
34
|
+
readonly limit?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* ページネーション付きの結果
|
|
39
|
+
*/
|
|
40
|
+
export interface PaginatedResult<T> {
|
|
41
|
+
/** 結果アイテム */
|
|
42
|
+
readonly items: readonly T[];
|
|
43
|
+
/** 総件数 */
|
|
44
|
+
readonly total: number;
|
|
45
|
+
/** 現在のページ番号 */
|
|
46
|
+
readonly page: number;
|
|
47
|
+
/** 1ページあたりの件数 */
|
|
48
|
+
readonly limit: number;
|
|
49
|
+
/** 総ページ数 */
|
|
50
|
+
readonly totalPages: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* ユーザーリポジトリインターフェース
|
|
55
|
+
*/
|
|
56
|
+
export interface IUserRepository {
|
|
57
|
+
/**
|
|
58
|
+
* 検索条件に基づいてユーザーを検索する
|
|
59
|
+
*
|
|
60
|
+
* @param criteria - 検索条件
|
|
61
|
+
* @param pagination - ページネーションオプション
|
|
62
|
+
* @returns ページネーション付きのユーザーリスト
|
|
63
|
+
*/
|
|
64
|
+
search(
|
|
65
|
+
criteria: UserSearchCriteria,
|
|
66
|
+
pagination?: PaginationOptions,
|
|
67
|
+
): Promise<Result<PaginatedResult<User>, Error>>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 検索条件に一致する単一のユーザーを取得する
|
|
71
|
+
*
|
|
72
|
+
* @param criteria - 検索条件
|
|
73
|
+
* @returns 見つかったユーザー、または見つからない場合はnull
|
|
74
|
+
*/
|
|
75
|
+
find(criteria: UserSearchCriteria): Promise<Result<User | null, Error>>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* IDでユーザーを取得する
|
|
79
|
+
*
|
|
80
|
+
* @param id - ユーザーID
|
|
81
|
+
* @returns 見つかったユーザー、または見つからない場合はnull
|
|
82
|
+
*/
|
|
83
|
+
findById(id: string): Promise<Result<User | null, Error>>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* メールアドレスでユーザーを取得する
|
|
87
|
+
*
|
|
88
|
+
* @param email - メールアドレス
|
|
89
|
+
* @returns 見つかったユーザー、または見つからない場合はnull
|
|
90
|
+
*/
|
|
91
|
+
findByEmail(email: string): Promise<Result<User | null, Error>>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* ユーザーの最終ログイン日時を更新する
|
|
95
|
+
*
|
|
96
|
+
* @param id - ユーザーID
|
|
97
|
+
* @param loginTime - ログイン日時
|
|
98
|
+
* @returns 成功時はvoid、失敗時はエラー
|
|
99
|
+
*/
|
|
100
|
+
updateLastLogin(id: string, loginTime: Date): Promise<Result<void, Error>>;
|
|
101
|
+
}
|
package/templates/turborepo-pandacss/packages/server-core/src/infrastructure/database/client.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
|
|
3
|
+
const globalForPrisma = globalThis as unknown as {
|
|
4
|
+
prisma: PrismaClient | undefined;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const prisma =
|
|
8
|
+
globalForPrisma.prisma ??
|
|
9
|
+
new PrismaClient({
|
|
10
|
+
log: ["query"],
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
|
|
14
|
+
|
|
15
|
+
export * from "@prisma/client";
|