import { withSpan, withCapture, INSTRUMENTATION_SYMBOLS, type ITracer, type ILogger, } from "@repo/core-shared/instrumentation"; import type { BindContext } from "@repo/core-shared/di"; import { mediaContainer } from "./container.js"; import { MEDIA_SYMBOLS } from "./symbols.js"; import { MockMediaRepository } from "../infrastructure/repositories/media.repository.mock.js"; import { buildDevMedia } from "../__seeds__/dev.js"; import { getMediaUseCase } from "../application/use-cases/get-media.use-case.js"; import { listMediaUseCase } from "../application/use-cases/list-media.use-case.js"; import { deleteMediaUseCase } from "../application/use-cases/delete-media.use-case.js"; import { getMediaController } from "../interface-adapters/controllers/get-media.controller.js"; import { listMediaController } from "../interface-adapters/controllers/list-media.controller.js"; import { deleteMediaController } from "../interface-adapters/controllers/delete-media.controller.js"; import type { IMediaRepository } from "../application/repositories/media.repository.interface.js"; /** * Replace the default empty mock with a populated one for dev mode + storybook. * * Call this from app boot when `USE_DEV_SEED=true`, mutually exclusive with * `bindProductionMedia(config)`. Tests must NOT call this — they construct * `new MockMediaRepository()` 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 bindDevSeedMedia(ctx: BindContext): Promise { const { tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx; // Bind shared instrumentation into feature container if (mediaContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) { mediaContainer.unbind(INSTRUMENTATION_SYMBOLS.TRACER); } if (mediaContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) { mediaContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER); } mediaContainer.bind(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer); mediaContainer.bind(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger); if (mediaContainer.isBound(MEDIA_SYMBOLS.IMediaRepository)) { mediaContainer.unbind(MEDIA_SYMBOLS.IMediaRepository); } const repo = new MockMediaRepository(tracer, logger); for (const media of buildDevMedia()) { await repo._store(media); } mediaContainer .bind(MEDIA_SYMBOLS.IMediaRepository) .toConstantValue(repo); // Wrap use cases + controllers identically to bind-production const wrappedGetMedia = withSpan( tracer, { name: "media.getMedia", op: "use-case" }, withCapture( logger, { feature: "media", layer: "use-case", name: "media.getMedia" }, getMediaUseCase(repo), ), ); const wrappedListMedia = withSpan( tracer, { name: "media.listMedia", op: "use-case" }, withCapture( logger, { feature: "media", layer: "use-case", name: "media.listMedia" }, listMediaUseCase(repo), ), ); const wrappedDeleteMedia = withSpan( tracer, { name: "media.deleteMedia", op: "use-case" }, withCapture( logger, { feature: "media", layer: "use-case", name: "media.deleteMedia" }, deleteMediaUseCase(repo), ), ); for (const sym of [ MEDIA_SYMBOLS.IGetMediaUseCase, MEDIA_SYMBOLS.IListMediaUseCase, MEDIA_SYMBOLS.IDeleteMediaUseCase, MEDIA_SYMBOLS.IGetMediaController, MEDIA_SYMBOLS.IListMediaController, MEDIA_SYMBOLS.IDeleteMediaController, ]) { if (mediaContainer.isBound(sym)) mediaContainer.unbind(sym); } mediaContainer.bind(MEDIA_SYMBOLS.IGetMediaUseCase).toConstantValue(wrappedGetMedia); mediaContainer.bind(MEDIA_SYMBOLS.IListMediaUseCase).toConstantValue(wrappedListMedia); mediaContainer.bind(MEDIA_SYMBOLS.IDeleteMediaUseCase).toConstantValue(wrappedDeleteMedia); mediaContainer .bind(MEDIA_SYMBOLS.IGetMediaController) .toConstantValue( withSpan( tracer, { name: "media.getMedia", op: "controller" }, withCapture( logger, { feature: "media", layer: "controller", name: "media.getMedia" }, getMediaController(wrappedGetMedia), ), ), ); mediaContainer .bind(MEDIA_SYMBOLS.IListMediaController) .toConstantValue( withSpan( tracer, { name: "media.listMedia", op: "controller" }, withCapture( logger, { feature: "media", layer: "controller", name: "media.listMedia" }, listMediaController(wrappedListMedia), ), ), ); mediaContainer .bind(MEDIA_SYMBOLS.IDeleteMediaController) .toConstantValue( withSpan( tracer, { name: "media.deleteMedia", op: "controller" }, withCapture( logger, { feature: "media", layer: "controller", name: "media.deleteMedia" }, deleteMediaController(wrappedDeleteMedia), ), ), ); // bus + queue are accept-and-forward in Phase 6; consumed by Phase 7 generator // output at the / anchors below. void bus; void queue; void realtime; void realtimeRegistry; // // // }