feat(web-next): bindAll wires (realtime, realtimeRegistry) through every feature
Extends bindAll/bindAllProduction/bindAllDevSeed to accept BindAllDeps and thread realtime + realtimeRegistry as the 6th and 7th positional args into each per-feature binder. Adds bindRealtimeBridge stub (empty allowlist in v1). bindAll accepts optional deps with InMemoryRealtimeBroadcaster fallback so existing page-level callers stay type-safe. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import { describe, it, expect, beforeEach } from "vitest";
|
import { describe, it, expect, beforeEach } from "vitest";
|
||||||
import { bindAllDevSeed, __resetBindStateForTests } from "@/server/bind-production";
|
import { bindAllDevSeed, __resetBindStateForTests } from "@/server/bind-production";
|
||||||
|
import { RecordingRealtimeBroadcaster } from "@repo/core-testing/instrumentation";
|
||||||
|
import { RealtimeHandlerRegistry } from "@repo/core-realtime";
|
||||||
import { authContainer } from "@repo/auth/di/container";
|
import { authContainer } from "@repo/auth/di/container";
|
||||||
import { AUTH_SYMBOLS } from "@repo/auth/di/symbols";
|
import { AUTH_SYMBOLS } from "@repo/auth/di/symbols";
|
||||||
import type { ISignUpController } from "@repo/auth";
|
import type { ISignUpController } from "@repo/auth";
|
||||||
@@ -20,7 +22,10 @@ describe("e2e: sign-up triggers welcome email via cross-feature event", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("delivers a welcome email after a successful sign-up", async () => {
|
it("delivers a welcome email after a successful sign-up", async () => {
|
||||||
await bindAllDevSeed();
|
await bindAllDevSeed({
|
||||||
|
realtime: new RecordingRealtimeBroadcaster(),
|
||||||
|
realtimeRegistry: new RealtimeHandlerRegistry(),
|
||||||
|
});
|
||||||
|
|
||||||
const mailer = marketingPagesContainer.get<RecordingMailerService>(
|
const mailer = marketingPagesContainer.get<RecordingMailerService>(
|
||||||
MARKETING_PAGES_SYMBOLS.IMailerService,
|
MARKETING_PAGES_SYMBOLS.IMailerService,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||||
|
import { RecordingRealtimeBroadcaster } from "@repo/core-testing/instrumentation";
|
||||||
|
import { RealtimeHandlerRegistry } from "@repo/core-realtime";
|
||||||
|
|
||||||
vi.mock("@repo/core-cms", () => ({ default: Promise.resolve({}) }));
|
vi.mock("@repo/core-cms", () => ({ default: Promise.resolve({}) }));
|
||||||
vi.mock("payload", () => ({ getPayload: vi.fn(async () => ({ jobs: { queue: vi.fn() } })) }));
|
vi.mock("payload", () => ({ getPayload: vi.fn(async () => ({ jobs: { queue: vi.fn() } })) }));
|
||||||
@@ -21,6 +23,13 @@ vi.mock("@repo/core-shared/instrumentation", async (importOriginal) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function makeDeps() {
|
||||||
|
return {
|
||||||
|
realtime: new RecordingRealtimeBroadcaster(),
|
||||||
|
realtimeRegistry: new RealtimeHandlerRegistry(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("bindAllProduction", () => {
|
describe("bindAllProduction", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
@@ -35,7 +44,7 @@ describe("bindAllProduction", () => {
|
|||||||
const { bindProductionNavigation } = await import("@repo/navigation/di/bind-production");
|
const { bindProductionNavigation } = await import("@repo/navigation/di/bind-production");
|
||||||
const { bindProductionMedia } = await import("@repo/media/di/bind-production");
|
const { bindProductionMedia } = await import("@repo/media/di/bind-production");
|
||||||
|
|
||||||
await bindAllProduction();
|
await bindAllProduction(makeDeps());
|
||||||
|
|
||||||
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
||||||
expect(bindProductionAuth).toHaveBeenCalledOnce();
|
expect(bindProductionAuth).toHaveBeenCalledOnce();
|
||||||
@@ -47,8 +56,9 @@ describe("bindAllProduction", () => {
|
|||||||
it("is idempotent — second call does not re-bind", async () => {
|
it("is idempotent — second call does not re-bind", async () => {
|
||||||
const { bindAllProduction } = await import("./bind-production");
|
const { bindAllProduction } = await import("./bind-production");
|
||||||
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
||||||
await bindAllProduction();
|
const deps = makeDeps();
|
||||||
await bindAllProduction();
|
await bindAllProduction(deps);
|
||||||
|
await bindAllProduction(deps);
|
||||||
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,13 +68,29 @@ describe("bindAllProduction", () => {
|
|||||||
const { PayloadJobsEventBus } = await import("@repo/core-events");
|
const { PayloadJobsEventBus } = await import("@repo/core-events");
|
||||||
const { PayloadJobQueue } = await import("@repo/core-shared/jobs");
|
const { PayloadJobQueue } = await import("@repo/core-shared/jobs");
|
||||||
|
|
||||||
await bindAllProduction();
|
await bindAllProduction(makeDeps());
|
||||||
|
|
||||||
const args = vi.mocked(bindProductionAuth).mock.calls[0]!;
|
const args = vi.mocked(bindProductionAuth).mock.calls[0]!;
|
||||||
// Args: (config, tracer, logger, bus, queue)
|
// Args: (config, tracer, logger, bus, queue, realtime, realtimeRegistry)
|
||||||
expect(args[3]).toBeInstanceOf(PayloadJobsEventBus);
|
expect(args[3]).toBeInstanceOf(PayloadJobsEventBus);
|
||||||
expect(args[4]).toBeInstanceOf(PayloadJobQueue);
|
expect(args[4]).toBeInstanceOf(PayloadJobQueue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("forwards realtime and realtimeRegistry as 6th + 7th args to each production binder", async () => {
|
||||||
|
const { RecordingRealtimeBroadcaster } = await import("@repo/core-testing/instrumentation");
|
||||||
|
const { RealtimeHandlerRegistry } = await import("@repo/core-realtime");
|
||||||
|
const { bindAllProduction } = await import("./bind-production");
|
||||||
|
const { bindProductionAuth } = await import("@repo/auth/di/bind-production");
|
||||||
|
|
||||||
|
const realtime = new RecordingRealtimeBroadcaster();
|
||||||
|
const realtimeRegistry = new RealtimeHandlerRegistry();
|
||||||
|
await bindAllProduction({ realtime, realtimeRegistry });
|
||||||
|
|
||||||
|
const args = vi.mocked(bindProductionAuth).mock.calls[0]!;
|
||||||
|
// Args: (config, tracer, logger, bus, queue, realtime, realtimeRegistry)
|
||||||
|
expect(args[5]).toBe(realtime);
|
||||||
|
expect(args[6]).toBe(realtimeRegistry);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("bindAllDevSeed", () => {
|
describe("bindAllDevSeed", () => {
|
||||||
@@ -79,13 +105,29 @@ describe("bindAllDevSeed", () => {
|
|||||||
const { InMemoryEventBus } = await import("@repo/core-events");
|
const { InMemoryEventBus } = await import("@repo/core-events");
|
||||||
const { InMemoryJobQueue } = await import("@repo/core-shared/jobs");
|
const { InMemoryJobQueue } = await import("@repo/core-shared/jobs");
|
||||||
|
|
||||||
await bindAllDevSeed();
|
await bindAllDevSeed(makeDeps());
|
||||||
|
|
||||||
const args = vi.mocked(bindDevSeedAuth).mock.calls[0]!;
|
const args = vi.mocked(bindDevSeedAuth).mock.calls[0]!;
|
||||||
// Args: (tracer, logger, bus, queue)
|
// Args: (tracer, logger, bus, queue, realtime, realtimeRegistry)
|
||||||
expect(args[2]).toBeInstanceOf(InMemoryEventBus);
|
expect(args[2]).toBeInstanceOf(InMemoryEventBus);
|
||||||
expect(args[3]).toBeInstanceOf(InMemoryJobQueue);
|
expect(args[3]).toBeInstanceOf(InMemoryJobQueue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("forwards realtime and realtimeRegistry as 5th + 6th args to each dev-seed binder", async () => {
|
||||||
|
const { RecordingRealtimeBroadcaster } = await import("@repo/core-testing/instrumentation");
|
||||||
|
const { RealtimeHandlerRegistry } = await import("@repo/core-realtime");
|
||||||
|
const { bindAllDevSeed } = await import("./bind-production");
|
||||||
|
const { bindDevSeedAuth } = await import("@repo/auth/di/bind-dev-seed");
|
||||||
|
|
||||||
|
const realtime = new RecordingRealtimeBroadcaster();
|
||||||
|
const realtimeRegistry = new RealtimeHandlerRegistry();
|
||||||
|
await bindAllDevSeed({ realtime, realtimeRegistry });
|
||||||
|
|
||||||
|
const args = vi.mocked(bindDevSeedAuth).mock.calls[0]!;
|
||||||
|
// Args: (tracer, logger, bus, queue, realtime, realtimeRegistry)
|
||||||
|
expect(args[4]).toBe(realtime);
|
||||||
|
expect(args[5]).toBe(realtimeRegistry);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("bindAll dispatcher", () => {
|
describe("bindAll dispatcher", () => {
|
||||||
@@ -106,7 +148,7 @@ describe("bindAll dispatcher", () => {
|
|||||||
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
||||||
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindDevSeedBlog).toHaveBeenCalledOnce();
|
expect(bindDevSeedBlog).toHaveBeenCalledOnce();
|
||||||
expect(bindProductionBlog).not.toHaveBeenCalled();
|
expect(bindProductionBlog).not.toHaveBeenCalled();
|
||||||
@@ -118,7 +160,7 @@ describe("bindAll dispatcher", () => {
|
|||||||
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
||||||
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
||||||
expect(bindDevSeedBlog).not.toHaveBeenCalled();
|
expect(bindDevSeedBlog).not.toHaveBeenCalled();
|
||||||
@@ -130,7 +172,7 @@ describe("bindAll dispatcher", () => {
|
|||||||
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
||||||
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindDevSeedBlog).toHaveBeenCalledOnce();
|
expect(bindDevSeedBlog).toHaveBeenCalledOnce();
|
||||||
expect(bindProductionBlog).not.toHaveBeenCalled();
|
expect(bindProductionBlog).not.toHaveBeenCalled();
|
||||||
@@ -143,7 +185,7 @@ describe("bindAll dispatcher", () => {
|
|||||||
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
||||||
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
||||||
expect(bindDevSeedBlog).not.toHaveBeenCalled();
|
expect(bindDevSeedBlog).not.toHaveBeenCalled();
|
||||||
@@ -169,7 +211,7 @@ describe("bindAll instrumentation orthogonality (Rule 0, R47)", () => {
|
|||||||
"@repo/core-shared/instrumentation"
|
"@repo/core-shared/instrumentation"
|
||||||
);
|
);
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindNoopInstrumentation).toHaveBeenCalledOnce();
|
expect(bindNoopInstrumentation).toHaveBeenCalledOnce();
|
||||||
expect(bindSentryInstrumentation).not.toHaveBeenCalled();
|
expect(bindSentryInstrumentation).not.toHaveBeenCalled();
|
||||||
@@ -183,7 +225,7 @@ describe("bindAll instrumentation orthogonality (Rule 0, R47)", () => {
|
|||||||
"@repo/core-shared/instrumentation"
|
"@repo/core-shared/instrumentation"
|
||||||
);
|
);
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindSentryInstrumentation).toHaveBeenCalledOnce();
|
expect(bindSentryInstrumentation).toHaveBeenCalledOnce();
|
||||||
expect(bindNoopInstrumentation).not.toHaveBeenCalled();
|
expect(bindNoopInstrumentation).not.toHaveBeenCalled();
|
||||||
@@ -196,7 +238,7 @@ describe("bindAll instrumentation orthogonality (Rule 0, R47)", () => {
|
|||||||
const { bindSentryInstrumentation } = await import("@repo/core-shared/instrumentation");
|
const { bindSentryInstrumentation } = await import("@repo/core-shared/instrumentation");
|
||||||
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
const { bindDevSeedBlog } = await import("@repo/blog/di/bind-dev-seed");
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindSentryInstrumentation).toHaveBeenCalledOnce();
|
expect(bindSentryInstrumentation).toHaveBeenCalledOnce();
|
||||||
expect(bindDevSeedBlog).toHaveBeenCalledOnce();
|
expect(bindDevSeedBlog).toHaveBeenCalledOnce();
|
||||||
@@ -209,7 +251,7 @@ describe("bindAll instrumentation orthogonality (Rule 0, R47)", () => {
|
|||||||
const { bindNoopInstrumentation } = await import("@repo/core-shared/instrumentation");
|
const { bindNoopInstrumentation } = await import("@repo/core-shared/instrumentation");
|
||||||
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
const { bindProductionBlog } = await import("@repo/blog/di/bind-production");
|
||||||
|
|
||||||
await bindAll();
|
await bindAll(makeDeps());
|
||||||
|
|
||||||
expect(bindNoopInstrumentation).toHaveBeenCalledOnce();
|
expect(bindNoopInstrumentation).toHaveBeenCalledOnce();
|
||||||
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
expect(bindProductionBlog).toHaveBeenCalledOnce();
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ import {
|
|||||||
PayloadJobQueue,
|
PayloadJobQueue,
|
||||||
type IJobQueue,
|
type IJobQueue,
|
||||||
} from "@repo/core-shared/jobs";
|
} from "@repo/core-shared/jobs";
|
||||||
|
import {
|
||||||
|
InMemoryRealtimeBroadcaster,
|
||||||
|
RealtimeHandlerRegistry,
|
||||||
|
type IRealtimeBroadcaster,
|
||||||
|
type IRealtimeHandlerRegistry,
|
||||||
|
} from "@repo/core-realtime";
|
||||||
import { bindProductionBlog } from "@repo/blog/di/bind-production";
|
import { bindProductionBlog } from "@repo/blog/di/bind-production";
|
||||||
import { bindProductionAuth } from "@repo/auth/di/bind-production";
|
import { bindProductionAuth } from "@repo/auth/di/bind-production";
|
||||||
import { bindProductionMarketingPages } from "@repo/marketing-pages/di/bind-production";
|
import { bindProductionMarketingPages } from "@repo/marketing-pages/di/bind-production";
|
||||||
@@ -31,6 +37,11 @@ import { bindDevSeedMarketingPages } from "@repo/marketing-pages/di/bind-dev-see
|
|||||||
import { bindDevSeedNavigation } from "@repo/navigation/di/bind-dev-seed";
|
import { bindDevSeedNavigation } from "@repo/navigation/di/bind-dev-seed";
|
||||||
import { bindDevSeedMedia } from "@repo/media/di/bind-dev-seed";
|
import { bindDevSeedMedia } from "@repo/media/di/bind-dev-seed";
|
||||||
|
|
||||||
|
type BindAllDeps = {
|
||||||
|
realtime: IRealtimeBroadcaster;
|
||||||
|
realtimeRegistry: IRealtimeHandlerRegistry;
|
||||||
|
};
|
||||||
|
|
||||||
let bound = false;
|
let bound = false;
|
||||||
|
|
||||||
// Shared container holds TRACER + LOGGER bindings; per-feature containers
|
// Shared container holds TRACER + LOGGER bindings; per-feature containers
|
||||||
@@ -97,17 +108,19 @@ function resolveEventsAndJobsDevSeed(): { bus: IEventBus; queue: IJobQueue } {
|
|||||||
* Payload-backed one. Constructs `new XRepository(config, tracer, logger)` per
|
* Payload-backed one. Constructs `new XRepository(config, tracer, logger)` per
|
||||||
* feature as Phase E feature wiring lands (blog: task 18; remaining: tasks 19–22).
|
* feature as Phase E feature wiring lands (blog: task 18; remaining: tasks 19–22).
|
||||||
*/
|
*/
|
||||||
export async function bindAllProduction(): Promise<void> {
|
export async function bindAllProduction(deps: BindAllDeps): Promise<void> {
|
||||||
if (bound) return;
|
if (bound) return;
|
||||||
bound = true;
|
bound = true;
|
||||||
const { tracer, logger } = resolveInstrumentation(); // Rule 0
|
const { tracer, logger } = resolveInstrumentation(); // Rule 0
|
||||||
const { bus, queue } = await resolveEventsAndJobsProduction();
|
const { bus, queue } = await resolveEventsAndJobsProduction();
|
||||||
const resolvedConfig = await config;
|
const resolvedConfig = await config;
|
||||||
bindProductionAuth(resolvedConfig, tracer, logger, bus, queue); // Phase E task 19
|
const { realtime, realtimeRegistry } = deps;
|
||||||
bindProductionBlog(resolvedConfig, tracer, logger, bus, queue); // Phase E task 18
|
bindProductionAuth(resolvedConfig, tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 19
|
||||||
bindProductionMarketingPages(resolvedConfig, tracer, logger, bus, queue); // Phase E task 20
|
bindProductionBlog(resolvedConfig, tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 18
|
||||||
bindProductionNavigation(resolvedConfig, tracer, logger, bus, queue); // Phase E task 21
|
bindProductionMarketingPages(resolvedConfig, tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 20
|
||||||
bindProductionMedia(resolvedConfig, tracer, logger, bus, queue); // Phase E task 22
|
bindProductionNavigation(resolvedConfig, tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 21
|
||||||
|
bindProductionMedia(resolvedConfig, tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 22
|
||||||
|
bindRealtimeBridge(bus, realtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,16 +128,18 @@ export async function bindAllProduction(): Promise<void> {
|
|||||||
* with realistic seed data so the running app shows non-empty UI without
|
* with realistic seed data so the running app shows non-empty UI without
|
||||||
* Payload booted. Mutually exclusive with `bindAllProduction()`.
|
* Payload booted. Mutually exclusive with `bindAllProduction()`.
|
||||||
*/
|
*/
|
||||||
export async function bindAllDevSeed(): Promise<void> {
|
export async function bindAllDevSeed(deps: BindAllDeps): Promise<void> {
|
||||||
if (bound) return;
|
if (bound) return;
|
||||||
bound = true;
|
bound = true;
|
||||||
const { tracer, logger } = resolveInstrumentation(); // Rule 0
|
const { tracer, logger } = resolveInstrumentation(); // Rule 0
|
||||||
const { bus, queue } = resolveEventsAndJobsDevSeed();
|
const { bus, queue } = resolveEventsAndJobsDevSeed();
|
||||||
await bindDevSeedAuth(tracer, logger, bus, queue); // Phase E task 19
|
const { realtime, realtimeRegistry } = deps;
|
||||||
await bindDevSeedBlog(tracer, logger, bus, queue); // Phase E task 18
|
await bindDevSeedAuth(tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 19
|
||||||
await bindDevSeedMarketingPages(tracer, logger, bus, queue); // Phase E task 20
|
await bindDevSeedBlog(tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 18
|
||||||
await bindDevSeedNavigation(tracer, logger, bus, queue); // Phase E task 21
|
await bindDevSeedMarketingPages(tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 20
|
||||||
await bindDevSeedMedia(tracer, logger, bus, queue); // Phase E task 22
|
await bindDevSeedNavigation(tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 21
|
||||||
|
await bindDevSeedMedia(tracer, logger, bus, queue, realtime, realtimeRegistry); // Phase E task 22
|
||||||
|
bindRealtimeBridge(bus, realtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,17 +153,33 @@ export async function bindAllDevSeed(): Promise<void> {
|
|||||||
* Rule 1: USE_DEV_SEED === "true" → dev seed (explicit override)
|
* Rule 1: USE_DEV_SEED === "true" → dev seed (explicit override)
|
||||||
* Rule 2: NODE_ENV === "production" → real Payload via bindAllProduction
|
* Rule 2: NODE_ENV === "production" → real Payload via bindAllProduction
|
||||||
* Rule 3: otherwise → dev seed (developer-friendly default)
|
* Rule 3: otherwise → dev seed (developer-friendly default)
|
||||||
|
*
|
||||||
|
* `deps` is optional; omitting it (page-level fallback, already-bound guard)
|
||||||
|
* uses in-memory noop implementations. In the custom-server flow, `server.ts`
|
||||||
|
* always provides Socket.IO-backed deps before any page request runs.
|
||||||
*/
|
*/
|
||||||
export async function bindAll(): Promise<void> {
|
export async function bindAll(deps?: Partial<BindAllDeps>): Promise<void> {
|
||||||
|
const resolvedDeps: BindAllDeps = {
|
||||||
|
realtime: deps?.realtime ?? new InMemoryRealtimeBroadcaster(),
|
||||||
|
realtimeRegistry: deps?.realtimeRegistry ?? new RealtimeHandlerRegistry(),
|
||||||
|
};
|
||||||
if (process.env.USE_DEV_SEED === "true") {
|
if (process.env.USE_DEV_SEED === "true") {
|
||||||
await bindAllDevSeed();
|
await bindAllDevSeed(resolvedDeps);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (process.env.NODE_ENV === "production") {
|
if (process.env.NODE_ENV === "production") {
|
||||||
await bindAllProduction();
|
await bindAllProduction(resolvedDeps);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await bindAllDevSeed();
|
await bindAllDevSeed(resolvedDeps);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindRealtimeBridge(_bus: IEventBus, _broadcaster: IRealtimeBroadcaster): void {
|
||||||
|
// v1 ships with an empty allowlist. The dashboard PR adds the first entries here.
|
||||||
|
// Example shape (commented out so v1 doesn't try to use it):
|
||||||
|
// bus.subscribe(userSignedUpEvent, "realtime-bridge", async (payload) =>
|
||||||
|
// broadcaster.broadcast(adminEventStreamChannel, { kind: "user.signed-up", payload }),
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test-only resets — not exported via package. Used by bind-production.test.ts. */
|
/** Test-only resets — not exported via package. Used by bind-production.test.ts. */
|
||||||
|
|||||||
Reference in New Issue
Block a user