fix: address Phase 1 spec review findings
- di-explainer.html: update stale JS data strings from positional-arg form (bindAllProduction(config), bindProductionBlog(config,tracer,...)) to ctx-arg form (bindAllProduction(deps), bindProductionBlog(ctx)) with a note on ctx shape. - auth/sign-up.use-case: change bus param from IEventBus to EventBusProtocol|undefined; guard bus.publish with if(bus) so the use case is safe when core-events is absent (Phase 3+). - auth/bind-production + bind-dev-seed: drop as IEventBus cast and unused IEventBus import; ctx.bus is now passed directly (typed as EventBusProtocol|undefined). - marketing-pages/bind-production + bind-dev-seed: drop as IJobQueue cast and unused IJobQueue import; wrap event-handler DI block in if(queue) guard so the handler is only bound when core-jobs is wired. - core-shared/src/index.ts: add `export * from "./di"` as the plan specified (subpath export alone is no longer the only access path). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1300,11 +1300,11 @@ const MODES = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
prod: {
|
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',
|
scenarioTag: 'when this happens',
|
||||||
title: 'Production binder ran at app boot',
|
title: 'Production binder ran at app boot',
|
||||||
narrative: [
|
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.",
|
"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."
|
"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."
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { z } from "zod";
|
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 { userSignedUpEvent } from "../../events/user-signed-up.event";
|
||||||
import { AuthenticationError } from "../../entities/errors/auth";
|
import { AuthenticationError } from "../../entities/errors/auth";
|
||||||
import { cookieSchema } from "../../entities/models/cookie";
|
import { cookieSchema } from "../../entities/models/cookie";
|
||||||
@@ -36,7 +36,7 @@ export const signUpUseCase =
|
|||||||
(
|
(
|
||||||
usersRepository: IUsersRepository,
|
usersRepository: IUsersRepository,
|
||||||
authenticationService: IAuthenticationService,
|
authenticationService: IAuthenticationService,
|
||||||
bus: IEventBus,
|
bus: EventBusProtocol | undefined,
|
||||||
) =>
|
) =>
|
||||||
async (input: SignUpInput): Promise<SignUpOutput> => {
|
async (input: SignUpInput): Promise<SignUpOutput> => {
|
||||||
const existingUser = await usersRepository.getUserByUsername(input.username);
|
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
|
// Auth is username-based — synthesize a deterministic email so the event
|
||||||
// payload validates against userSignedUpEventSchema.email().
|
// payload validates against userSignedUpEventSchema.email().
|
||||||
|
// bus is optional: absent when core-events is not wired (Phase 3+).
|
||||||
|
if (bus) {
|
||||||
await bus.publish(userSignedUpEvent, {
|
await bus.publish(userSignedUpEvent, {
|
||||||
userId: newUser.id,
|
userId: newUser.id,
|
||||||
email: `${newUser.username}@example.local`,
|
email: `${newUser.username}@example.local`,
|
||||||
signedUpAt: new Date().toISOString(),
|
signedUpAt: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return signUpOutputSchema.parse({ session, cookie });
|
return signUpOutputSchema.parse({ session, cookie });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
type ILogger,
|
type ILogger,
|
||||||
} from "@repo/core-shared/instrumentation";
|
} from "@repo/core-shared/instrumentation";
|
||||||
import type { BindContext } from "@repo/core-shared/di";
|
import type { BindContext } from "@repo/core-shared/di";
|
||||||
import type { IEventBus } from "@repo/core-events";
|
|
||||||
import { authContainer } from "./container.js";
|
import { authContainer } from "./container.js";
|
||||||
import { AUTH_SYMBOLS } from "./symbols.js";
|
import { AUTH_SYMBOLS } from "./symbols.js";
|
||||||
import { MockUsersRepository } from "../infrastructure/repositories/users.repository.mock.js";
|
import { MockUsersRepository } from "../infrastructure/repositories/users.repository.mock.js";
|
||||||
@@ -78,7 +77,7 @@ export async function bindDevSeedAuth(ctx: BindContext): Promise<void> {
|
|||||||
withCapture(
|
withCapture(
|
||||||
logger,
|
logger,
|
||||||
{ feature: "auth", layer: "use-case", name: "auth.signUp" },
|
{ feature: "auth", layer: "use-case", name: "auth.signUp" },
|
||||||
signUpUseCase(repo, authService, bus as IEventBus),
|
signUpUseCase(repo, authService, bus),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const wrappedSignOut = withSpan(
|
const wrappedSignOut = withSpan(
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
type ILogger,
|
type ILogger,
|
||||||
} from "@repo/core-shared/instrumentation";
|
} from "@repo/core-shared/instrumentation";
|
||||||
import type { BindProductionContext } from "@repo/core-shared/di";
|
import type { BindProductionContext } from "@repo/core-shared/di";
|
||||||
import type { IEventBus } from "@repo/core-events";
|
|
||||||
import { authContainer } from "./container";
|
import { authContainer } from "./container";
|
||||||
import { AUTH_SYMBOLS } from "./symbols";
|
import { AUTH_SYMBOLS } from "./symbols";
|
||||||
import { UsersRepository } from "../infrastructure/repositories/users.repository";
|
import { UsersRepository } from "../infrastructure/repositories/users.repository";
|
||||||
@@ -69,7 +68,7 @@ export function bindProductionAuth(ctx: BindProductionContext): void {
|
|||||||
withCapture(
|
withCapture(
|
||||||
logger,
|
logger,
|
||||||
{ feature: "auth", layer: "use-case", name: "auth.signUp" },
|
{ feature: "auth", layer: "use-case", name: "auth.signUp" },
|
||||||
signUpUseCase(repo, authService, bus as IEventBus),
|
signUpUseCase(repo, authService, bus),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const wrappedSignOut = withSpan(
|
const wrappedSignOut = withSpan(
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export { requireEnv } from "./lib/env";
|
export { requireEnv } from "./lib/env";
|
||||||
export { toIsoString } from "./lib/date";
|
export { toIsoString } from "./lib/date";
|
||||||
|
export * from "./di";
|
||||||
export * from "./instrumentation/index";
|
export * from "./instrumentation/index";
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
type ILogger,
|
type ILogger,
|
||||||
} from "@repo/core-shared/instrumentation";
|
} from "@repo/core-shared/instrumentation";
|
||||||
import type { BindContext } from "@repo/core-shared/di";
|
import type { BindContext } from "@repo/core-shared/di";
|
||||||
import type { IJobQueue } from "@repo/core-shared/jobs";
|
|
||||||
import { marketingPagesContainer } from "./container.js";
|
import { marketingPagesContainer } from "./container.js";
|
||||||
import { MARKETING_PAGES_SYMBOLS } from "./symbols.js";
|
import { MARKETING_PAGES_SYMBOLS } from "./symbols.js";
|
||||||
import { MockPagesRepository } from "../infrastructure/repositories/pages.repository.mock.js";
|
import { MockPagesRepository } from "../infrastructure/repositories/pages.repository.mock.js";
|
||||||
@@ -140,6 +139,8 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise<void>
|
|||||||
|
|
||||||
// <gen:event-handlers>
|
// <gen:event-handlers>
|
||||||
// onAuthUserSignedUpHandler subscription — generated, edit the handler file (not this block) for behavior.
|
// onAuthUserSignedUpHandler subscription — generated, edit the handler file (not this block) for behavior.
|
||||||
|
// queue is optional: guard so the handler is only bound when core-jobs is wired (Phase 3+).
|
||||||
|
if (queue) {
|
||||||
const wrappedAuthUserSignedUp = withSpan(
|
const wrappedAuthUserSignedUp = withSpan(
|
||||||
tracer,
|
tracer,
|
||||||
{ name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
|
{ name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
|
||||||
@@ -150,7 +151,7 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise<void>
|
|||||||
layer: "event-handler",
|
layer: "event-handler",
|
||||||
name: "marketing-pages.onAuthUserSignedUpHandler",
|
name: "marketing-pages.onAuthUserSignedUpHandler",
|
||||||
},
|
},
|
||||||
onAuthUserSignedUpHandler(queue as IJobQueue),
|
onAuthUserSignedUpHandler(queue),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
|
if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
|
||||||
@@ -158,6 +159,7 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise<void>
|
|||||||
}
|
}
|
||||||
marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
|
marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
|
||||||
bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
|
bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
|
||||||
|
}
|
||||||
// <gen:jobs>
|
// <gen:jobs>
|
||||||
const wrappedSendWelcomeEmail = withSpan(
|
const wrappedSendWelcomeEmail = withSpan(
|
||||||
tracer,
|
tracer,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
type ILogger,
|
type ILogger,
|
||||||
} from "@repo/core-shared/instrumentation";
|
} from "@repo/core-shared/instrumentation";
|
||||||
import type { BindProductionContext } from "@repo/core-shared/di";
|
import type { BindProductionContext } from "@repo/core-shared/di";
|
||||||
import type { IJobQueue } from "@repo/core-shared/jobs";
|
|
||||||
import { marketingPagesContainer } from "./container";
|
import { marketingPagesContainer } from "./container";
|
||||||
import { MARKETING_PAGES_SYMBOLS } from "./symbols";
|
import { MARKETING_PAGES_SYMBOLS } from "./symbols";
|
||||||
import { PagesRepository } from "../infrastructure/repositories/pages.repository";
|
import { PagesRepository } from "../infrastructure/repositories/pages.repository";
|
||||||
@@ -130,6 +129,8 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void {
|
|||||||
|
|
||||||
// <gen:event-handlers>
|
// <gen:event-handlers>
|
||||||
// onAuthUserSignedUpHandler subscription — generated, edit the handler file (not this block) for behavior.
|
// onAuthUserSignedUpHandler subscription — generated, edit the handler file (not this block) for behavior.
|
||||||
|
// queue is optional: guard so the handler is only bound when core-jobs is wired (Phase 3+).
|
||||||
|
if (queue) {
|
||||||
const wrappedAuthUserSignedUp = withSpan(
|
const wrappedAuthUserSignedUp = withSpan(
|
||||||
tracer,
|
tracer,
|
||||||
{ name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
|
{ name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" },
|
||||||
@@ -140,7 +141,7 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void {
|
|||||||
layer: "event-handler",
|
layer: "event-handler",
|
||||||
name: "marketing-pages.onAuthUserSignedUpHandler",
|
name: "marketing-pages.onAuthUserSignedUpHandler",
|
||||||
},
|
},
|
||||||
onAuthUserSignedUpHandler(queue as IJobQueue),
|
onAuthUserSignedUpHandler(queue),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
|
if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) {
|
||||||
@@ -148,6 +149,7 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void {
|
|||||||
}
|
}
|
||||||
marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
|
marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp);
|
||||||
bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
|
bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp);
|
||||||
|
}
|
||||||
// <gen:jobs>
|
// <gen:jobs>
|
||||||
const wrappedSendWelcomeEmail = withSpan(
|
const wrappedSendWelcomeEmail = withSpan(
|
||||||
tracer,
|
tracer,
|
||||||
|
|||||||
Reference in New Issue
Block a user