diff --git a/docs/architecture/di-explainer.html b/docs/architecture/di-explainer.html
index 9631de9..3479209 100644
--- a/docs/architecture/di-explainer.html
+++ b/docs/architecture/di-explainer.html
@@ -1300,11 +1300,11 @@ const MODES = {
],
},
prod: {
- flag: 'NODE_ENV="production", USE_DEV_SEED unset · bindAllProduction(config) ran',
+ flag: 'NODE_ENV="production", USE_DEV_SEED unset · bindAllProduction(deps) ran',
scenarioTag: 'when this happens',
title: 'Production binder ran at app boot',
narrative: [
- "App's bindAll() did not see USE_DEV_SEED=true, so it dispatched to bindAllProduction(). That resolved instrumentation (Rule 0), resolved a Payload-backed bus + queue (ADR-015), awaited the Payload config, and called bindProductionBlog(config, tracer, logger, bus, queue).",
+ "App's bindAll() did not see USE_DEV_SEED=true, so it dispatched to bindAllProduction(). That resolved instrumentation (Rule 0), resolved a Payload-backed bus + queue (ADR-015), awaited the Payload config, and called bindProductionBlog(ctx) where ctx carries { config, tracer, logger, bus?, queue?, realtime?, realtimeRegistry? }.",
"The binder unbound IArticlesRepository and rebound it with .toConstantValue(new ArticlesRepository(config, tracer, logger)) — the real Payload-backed implementation that will hit Postgres on every method call.",
"Use cases and controllers still resolve through their .toDynamicValue closures, but the closures now fetch the real repo. Callers cannot tell the difference — same interface, different storage."
],
diff --git a/packages/auth/src/application/use-cases/sign-up.use-case.ts b/packages/auth/src/application/use-cases/sign-up.use-case.ts
index 54bf9fe..c40c999 100644
--- a/packages/auth/src/application/use-cases/sign-up.use-case.ts
+++ b/packages/auth/src/application/use-cases/sign-up.use-case.ts
@@ -1,6 +1,6 @@
import { z } from "zod";
-import type { IEventBus } from "@repo/core-events";
+import type { EventBusProtocol } from "@repo/core-shared/di";
import { userSignedUpEvent } from "../../events/user-signed-up.event";
import { AuthenticationError } from "../../entities/errors/auth";
import { cookieSchema } from "../../entities/models/cookie";
@@ -36,7 +36,7 @@ export const signUpUseCase =
(
usersRepository: IUsersRepository,
authenticationService: IAuthenticationService,
- bus: IEventBus,
+ bus: EventBusProtocol | undefined,
) =>
async (input: SignUpInput): Promise => {
const existingUser = await usersRepository.getUserByUsername(input.username);
@@ -57,11 +57,14 @@ export const signUpUseCase =
// Auth is username-based — synthesize a deterministic email so the event
// payload validates against userSignedUpEventSchema.email().
- await bus.publish(userSignedUpEvent, {
- userId: newUser.id,
- email: `${newUser.username}@example.local`,
- signedUpAt: new Date().toISOString(),
- });
+ // bus is optional: absent when core-events is not wired (Phase 3+).
+ if (bus) {
+ await bus.publish(userSignedUpEvent, {
+ userId: newUser.id,
+ email: `${newUser.username}@example.local`,
+ signedUpAt: new Date().toISOString(),
+ });
+ }
return signUpOutputSchema.parse({ session, cookie });
};
diff --git a/packages/auth/src/di/bind-dev-seed.ts b/packages/auth/src/di/bind-dev-seed.ts
index 3bbacb8..e0ee6d4 100644
--- a/packages/auth/src/di/bind-dev-seed.ts
+++ b/packages/auth/src/di/bind-dev-seed.ts
@@ -6,7 +6,6 @@ import {
type ILogger,
} from "@repo/core-shared/instrumentation";
import type { BindContext } from "@repo/core-shared/di";
-import type { IEventBus } from "@repo/core-events";
import { authContainer } from "./container.js";
import { AUTH_SYMBOLS } from "./symbols.js";
import { MockUsersRepository } from "../infrastructure/repositories/users.repository.mock.js";
@@ -78,7 +77,7 @@ export async function bindDevSeedAuth(ctx: BindContext): Promise {
withCapture(
logger,
{ feature: "auth", layer: "use-case", name: "auth.signUp" },
- signUpUseCase(repo, authService, bus as IEventBus),
+ signUpUseCase(repo, authService, bus),
),
);
const wrappedSignOut = withSpan(
diff --git a/packages/auth/src/di/bind-production.ts b/packages/auth/src/di/bind-production.ts
index 6236167..2af12a1 100644
--- a/packages/auth/src/di/bind-production.ts
+++ b/packages/auth/src/di/bind-production.ts
@@ -6,7 +6,6 @@ import {
type ILogger,
} from "@repo/core-shared/instrumentation";
import type { BindProductionContext } from "@repo/core-shared/di";
-import type { IEventBus } from "@repo/core-events";
import { authContainer } from "./container";
import { AUTH_SYMBOLS } from "./symbols";
import { UsersRepository } from "../infrastructure/repositories/users.repository";
@@ -69,7 +68,7 @@ export function bindProductionAuth(ctx: BindProductionContext): void {
withCapture(
logger,
{ feature: "auth", layer: "use-case", name: "auth.signUp" },
- signUpUseCase(repo, authService, bus as IEventBus),
+ signUpUseCase(repo, authService, bus),
),
);
const wrappedSignOut = withSpan(
diff --git a/packages/core-shared/src/index.ts b/packages/core-shared/src/index.ts
index 0c98e6c..261b0c4 100644
--- a/packages/core-shared/src/index.ts
+++ b/packages/core-shared/src/index.ts
@@ -1,3 +1,4 @@
export { requireEnv } from "./lib/env";
export { toIsoString } from "./lib/date";
+export * from "./di";
export * from "./instrumentation/index";
diff --git a/packages/marketing-pages/src/di/bind-dev-seed.ts b/packages/marketing-pages/src/di/bind-dev-seed.ts
index fc3c69d..5fb97bb 100644
--- a/packages/marketing-pages/src/di/bind-dev-seed.ts
+++ b/packages/marketing-pages/src/di/bind-dev-seed.ts
@@ -6,7 +6,6 @@ import {
type ILogger,
} from "@repo/core-shared/instrumentation";
import type { BindContext } from "@repo/core-shared/di";
-import type { IJobQueue } from "@repo/core-shared/jobs";
import { marketingPagesContainer } from "./container.js";
import { MARKETING_PAGES_SYMBOLS } from "./symbols.js";
import { MockPagesRepository } from "../infrastructure/repositories/pages.repository.mock.js";
@@ -140,24 +139,27 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise
//
// onAuthUserSignedUpHandler subscription — generated, edit the handler file (not this block) for behavior.
- const wrappedAuthUserSignedUp = withSpan(
- tracer,
- { name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
- withCapture(
- logger,
- {
- feature: "marketing-pages",
- layer: "event-handler",
- name: "marketing-pages.onAuthUserSignedUpHandler",
- },
- onAuthUserSignedUpHandler(queue as IJobQueue),
- ),
- );
- if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
- marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler);
+ // queue is optional: guard so the handler is only bound when core-jobs is wired (Phase 3+).
+ if (queue) {
+ const wrappedAuthUserSignedUp = withSpan(
+ tracer,
+ { name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
+ withCapture(
+ logger,
+ {
+ feature: "marketing-pages",
+ layer: "event-handler",
+ name: "marketing-pages.onAuthUserSignedUpHandler",
+ },
+ onAuthUserSignedUpHandler(queue),
+ ),
+ );
+ if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
+ marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler);
+ }
+ marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
+ bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
}
- marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
- bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
//
const wrappedSendWelcomeEmail = withSpan(
tracer,
diff --git a/packages/marketing-pages/src/di/bind-production.ts b/packages/marketing-pages/src/di/bind-production.ts
index ef0af91..4447753 100644
--- a/packages/marketing-pages/src/di/bind-production.ts
+++ b/packages/marketing-pages/src/di/bind-production.ts
@@ -6,7 +6,6 @@ import {
type ILogger,
} from "@repo/core-shared/instrumentation";
import type { BindProductionContext } from "@repo/core-shared/di";
-import type { IJobQueue } from "@repo/core-shared/jobs";
import { marketingPagesContainer } from "./container";
import { MARKETING_PAGES_SYMBOLS } from "./symbols";
import { PagesRepository } from "../infrastructure/repositories/pages.repository";
@@ -130,24 +129,27 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void {
//
// onAuthUserSignedUpHandler subscription — generated, edit the handler file (not this block) for behavior.
- const wrappedAuthUserSignedUp = withSpan(
- tracer,
- { name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
- withCapture(
- logger,
- {
- feature: "marketing-pages",
- layer: "event-handler",
- name: "marketing-pages.onAuthUserSignedUpHandler",
- },
- onAuthUserSignedUpHandler(queue as IJobQueue),
- ),
- );
- if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
- marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler);
+ // queue is optional: guard so the handler is only bound when core-jobs is wired (Phase 3+).
+ if (queue) {
+ const wrappedAuthUserSignedUp = withSpan(
+ tracer,
+ { name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
+ withCapture(
+ logger,
+ {
+ feature: "marketing-pages",
+ layer: "event-handler",
+ name: "marketing-pages.onAuthUserSignedUpHandler",
+ },
+ onAuthUserSignedUpHandler(queue),
+ ),
+ );
+ if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
+ marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler);
+ }
+ marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
+ bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
}
- marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
- bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
//
const wrappedSendWelcomeEmail = withSpan(
tracer,