import { withSpan, withCapture, INSTRUMENTATION_SYMBOLS, type ITracer, type ILogger, } from "@repo/core-shared/instrumentation"; import type { BindContext } from "@repo/core-shared/di"; import { assertFeatureConformance, wireUseCase, } from "@repo/core-shared/conformance"; import { authManifest } from "../feature.manifest.js"; import { authContainer } from "./container.js"; import { AUTH_SYMBOLS } from "./symbols.js"; import { MockUsersRepository } from "../infrastructure/repositories/users.repository.mock.js"; import { buildDevUsers } from "../__seeds__/dev.js"; import { signInUseCase } from "../application/use-cases/sign-in.use-case.js"; import { signUpUseCase } from "../application/use-cases/sign-up.use-case.js"; import { signOutUseCase } from "../application/use-cases/sign-out.use-case.js"; import { signInController } from "../interface-adapters/controllers/sign-in.controller.js"; import { signUpController } from "../interface-adapters/controllers/sign-up.controller.js"; import { signOutController } from "../interface-adapters/controllers/sign-out.controller.js"; import type { IUsersRepository } from "../application/repositories/users.repository.interface.js"; import type { IAuthenticationService } from "../application/services/authentication.service.interface.js"; /** * Replace the default mock with a populated one for dev mode + storybook. * * Call this from app boot when `USE_DEV_SEED=true`, mutually exclusive with * `bindProductionAuth(config)`. Tests must NOT call this — they construct * `new MockUsersRepository()` directly and seed via factories per-test. * * The `IAuthenticationService` binding is left untouched; it resolves users * through DI from the newly seeded repo. * * Idempotent: safe to call multiple times; each call rebuilds a fresh * populated repo and rebinds the symbol. */ export async function bindDevSeedAuth(ctx: BindContext): Promise { const { tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx; // Bind shared instrumentation into feature container if (authContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) { authContainer.unbind(INSTRUMENTATION_SYMBOLS.TRACER); } if (authContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) { authContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER); } authContainer .bind(INSTRUMENTATION_SYMBOLS.TRACER) .toConstantValue(tracer); authContainer .bind(INSTRUMENTATION_SYMBOLS.LOGGER) .toConstantValue(logger); if (authContainer.isBound(AUTH_SYMBOLS.IUsersRepository)) { authContainer.unbind(AUTH_SYMBOLS.IUsersRepository); } const repo = new MockUsersRepository([], tracer, logger); for (const user of buildDevUsers()) { await repo.createUser(user); } authContainer .bind(AUTH_SYMBOLS.IUsersRepository) .toConstantValue(repo); // Need auth service from container for use cases const authService = authContainer.get( AUTH_SYMBOLS.IAuthenticationService, ); // Use cases const wrappedSignIn = wireUseCase({ container: authContainer, symbol: AUTH_SYMBOLS.ISignInUseCase, factory: signInUseCase, deps: [repo, authService], feature: "auth", layer: "use-case", name: "signIn", tracer, logger, }); const wrappedSignUp = wireUseCase({ container: authContainer, symbol: AUTH_SYMBOLS.ISignUpUseCase, factory: signUpUseCase, deps: [repo, authService, bus], feature: "auth", layer: "use-case", name: "signUp", tracer, logger, }); const wrappedSignOut = wireUseCase({ container: authContainer, symbol: AUTH_SYMBOLS.ISignOutUseCase, factory: signOutUseCase, deps: [authService], feature: "auth", layer: "use-case", name: "signOut", tracer, logger, }); // Controllers for (const sym of [ AUTH_SYMBOLS.ISignInController, AUTH_SYMBOLS.ISignUpController, AUTH_SYMBOLS.ISignOutController, ]) { if (authContainer.isBound(sym)) authContainer.unbind(sym); } authContainer .bind(AUTH_SYMBOLS.ISignInController) .toConstantValue( withSpan( tracer, { name: "auth.signIn", op: "controller" }, withCapture( logger, { feature: "auth", layer: "controller", name: "auth.signIn" }, signInController(wrappedSignIn), ), ), ); authContainer .bind(AUTH_SYMBOLS.ISignUpController) .toConstantValue( withSpan( tracer, { name: "auth.signUp", op: "controller" }, withCapture( logger, { feature: "auth", layer: "controller", name: "auth.signUp" }, signUpController(wrappedSignUp), ), ), ); authContainer .bind(AUTH_SYMBOLS.ISignOutController) .toConstantValue( withSpan( tracer, { name: "auth.signOut", op: "controller" }, withCapture( logger, { feature: "auth", layer: "controller", name: "auth.signOut" }, signOutController(wrappedSignOut), ), ), ); // bus + queue are passed through; generated handlers consume them at the anchors below. void bus; void queue; void realtime; void realtimeRegistry; // // // // Boot-time conformance check (dev-seed mode). assertFeatureConformance( authContainer, authManifest, { signIn: AUTH_SYMBOLS.ISignInUseCase, signUp: AUTH_SYMBOLS.ISignUpUseCase, signOut: AUTH_SYMBOLS.ISignOutUseCase, }, ctx, ); }