diff --git a/apps/web-next/next.config.mjs b/apps/web-next/next.config.mjs index eaa8607..fa80bf2 100644 --- a/apps/web-next/next.config.mjs +++ b/apps/web-next/next.config.mjs @@ -7,6 +7,8 @@ const nextConfig = { "@repo/blog", "@repo/core-api", "@repo/core-cms", + "@repo/core-events", + "@repo/core-realtime", "@repo/core-shared", "@repo/core-trpc", "@repo/core-ui", diff --git a/apps/web-next/package.json b/apps/web-next/package.json index 90f30d0..0614b3b 100644 --- a/apps/web-next/package.json +++ b/apps/web-next/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build": "echo 'Next.js build requires full environment — use pnpm dev or docker'", - "dev": "tsx server.ts", + "dev": "TSX_TSCONFIG_PATH=../../tsconfig.json tsx server.ts", "start": "node --import tsx server.ts", "lint": "eslint .", "test": "vitest run --passWithNoTests", diff --git a/apps/web-next/server.ts b/apps/web-next/server.ts index d271648..9a4959b 100644 --- a/apps/web-next/server.ts +++ b/apps/web-next/server.ts @@ -11,7 +11,6 @@ import { type IRealtimeAuthenticator, } from "@repo/core-realtime"; import { SESSION_COOKIE } from "@repo/auth"; -import { authContainer } from "@repo/auth/di/container"; import { AUTH_SYMBOLS } from "@repo/auth/di/symbols"; import { bindAll } from "./src/server/bind-production.js"; @@ -35,6 +34,11 @@ const authenticator: IRealtimeAuthenticator = { authenticate: async ({ cookies }) => { const sessionId = cookies[SESSION_COOKIE]; if (!sessionId) return null; + // Lazy-import the auth container after bindAll() has already populated it. + // Dynamic import defers tsx's module transformation to runtime, which lets + // reflect-metadata and the DI decorators resolve correctly in this server + // entry point. + const { authContainer } = await import("@repo/auth/di/container"); const authService = authContainer.get<{ validateSession: (id: string) => Promise<{ user: { id: string }; session: unknown } | null>; }>(AUTH_SYMBOLS.IAuthenticationService); diff --git a/packages/core-eslint/rules/no-direct-socket-io.js b/packages/core-eslint/rules/no-direct-socket-io.js index 6e854f5..63448a6 100644 --- a/packages/core-eslint/rules/no-direct-socket-io.js +++ b/packages/core-eslint/rules/no-direct-socket-io.js @@ -2,6 +2,7 @@ const ALLOWED = [ /\/packages\/core-realtime\/src\//, /\/apps\/[^/]+\/server\.ts$/, + /\/apps\/[^/]+\/src\/.*\.test\.ts$/, ]; export default { diff --git a/packages/core-eslint/rules/no-direct-socket-io.test.js b/packages/core-eslint/rules/no-direct-socket-io.test.js index 2837fce..bf74953 100644 --- a/packages/core-eslint/rules/no-direct-socket-io.test.js +++ b/packages/core-eslint/rules/no-direct-socket-io.test.js @@ -12,6 +12,9 @@ tester.run("no-direct-socket-io", rule, { { code: 'import { Server } from "socket.io";', filename: "/repo/packages/core-realtime/src/socket-io-realtime-server.ts" }, // Allowed in app servers { code: 'import { Server } from "socket.io";', filename: "/repo/apps/web-next/server.ts" }, + // Allowed in app integration tests (e.g. realtime-ping e2e) + { code: 'import { Server } from "socket.io";', filename: "/repo/apps/web-next/src/__tests__/realtime-ping.test.ts" }, + { code: 'import { io } from "socket.io-client";', filename: "/repo/apps/web-next/src/__tests__/realtime-ping.test.ts" }, // Allowed elsewhere when not importing socket.io { code: 'import { foo } from "bar";', filename: "/repo/packages/blog/src/foo.ts" }, ], diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0caab9a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true + } +}