import { withSpan, withCapture, INSTRUMENTATION_SYMBOLS, type ITracer, type ILogger, } from "@repo/core-shared/instrumentation"; import type { IEventBus } from "@repo/core-events"; import type { IJobQueue } from "@repo/core-shared/jobs"; import { navigationContainer } from "./container.js"; import { NAVIGATION_SYMBOLS } from "./symbols.js"; import { MockHeaderRepository } from "../infrastructure/repositories/header.repository.mock.js"; import { buildDevHeader } from "../__seeds__/dev.js"; import { getHeaderUseCase } from "../application/use-cases/get-header.use-case.js"; import { getHeaderController } from "../interface-adapters/controllers/get-header.controller.js"; import type { IHeaderRepository } from "../application/repositories/header.repository.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 * `bindProductionNavigation(config)`. Tests must NOT call this — they * construct `new MockHeaderRepository()` directly and seed via factories * per-test. * * Idempotent: safe to call multiple times; each call rebuilds a fresh * populated repo and rebinds the symbol. */ export async function bindDevSeedNavigation( tracer: ITracer, logger: ILogger, bus: IEventBus, queue: IJobQueue, ): Promise { // Bind shared instrumentation into feature container if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) { navigationContainer.unbind(INSTRUMENTATION_SYMBOLS.TRACER); } if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) { navigationContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER); } navigationContainer.bind(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer); navigationContainer.bind(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger); if (navigationContainer.isBound(NAVIGATION_SYMBOLS.IHeaderRepository)) { navigationContainer.unbind(NAVIGATION_SYMBOLS.IHeaderRepository); } const repo = new MockHeaderRepository(buildDevHeader(), tracer, logger); navigationContainer .bind(NAVIGATION_SYMBOLS.IHeaderRepository) .toConstantValue(repo); // Wrap use case + controller identically to bind-production const wrappedGetHeader = withSpan( tracer, { name: "navigation.getHeader", op: "use-case" }, withCapture( logger, { feature: "navigation", layer: "use-case", name: "navigation.getHeader" }, getHeaderUseCase(repo), ), ); for (const sym of [ NAVIGATION_SYMBOLS.IGetHeaderUseCase, NAVIGATION_SYMBOLS.IGetHeaderController, ]) { if (navigationContainer.isBound(sym)) navigationContainer.unbind(sym); } navigationContainer .bind(NAVIGATION_SYMBOLS.IGetHeaderUseCase) .toConstantValue(wrappedGetHeader); navigationContainer .bind(NAVIGATION_SYMBOLS.IGetHeaderController) .toConstantValue( withSpan( tracer, { name: "navigation.getHeader", op: "controller" }, withCapture( logger, { feature: "navigation", layer: "controller", name: "navigation.getHeader" }, getHeaderController(wrappedGetHeader), ), ), ); // bus + queue are accept-and-forward in Phase 6; consumed by Phase 7 generator // output at the / anchors below. void bus; void queue; // // // }