refactor: remove core-events from main (scaffoldable via gen core-package events)
- Delete packages/core-events/ (15 files) - Strip @repo/core-events from all 5 feature package.json + apps/web-next/package.json - Strip @repo/core-events from apps/web-next/next.config.mjs transpilePackages - Strip E1 + J no-restricted-syntax blocks from core-eslint/base.js (anchor remains) - Update bind-production.ts: drop bus construction + IEventBus import; rename resolveEventsAndJobsProduction → resolveJobsProduction (queue only), resolveEventsAndJobsDevSeed → resolveJobsDevSeed (queue only); ctx no longer has bus field; BindProductionContext generic arg narrowed - Update bind-production.test.ts: assert ctx.bus is undefined, drop PayloadJobsEventBus/InMemoryEventBus instanceof checks - Update sign-up-welcome-email.test.ts: assert mailer stays empty without bus - Inline userSignedUpEvent in auth (drop defineEvent import from core-events) - Drop InMemoryEventBus fallback from auth/di/module.ts Feature binders' bus?.subscribe/publish calls remain as no-ops. Scaffold @repo/core-events back via: pnpm turbo gen core-package events Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/core-events": "workspace:*",
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"inversify": "^6.2.0",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ContainerModule, type interfaces } from "inversify";
|
||||
import { InMemoryEventBus } from "@repo/core-events";
|
||||
|
||||
import type { IUsersRepository } from "../application/repositories/users.repository.interface";
|
||||
import type { IAuthenticationService } from "../application/services/authentication.service.interface";
|
||||
@@ -45,13 +44,13 @@ export const AuthModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
);
|
||||
|
||||
bind<ISignUpUseCase>(AUTH_SYMBOLS.ISignUpUseCase).toDynamicValue((ctx) =>
|
||||
// Default fallback uses a fresh InMemoryEventBus — real cross-feature
|
||||
// wiring runs through bindProductionAuth / bindDevSeedAuth where the
|
||||
// bindAll() dispatcher passes a shared bus instance.
|
||||
// No default bus — real cross-feature wiring runs through
|
||||
// bindProductionAuth / bindDevSeedAuth where bindAll() passes a shared
|
||||
// bus instance when @repo/core-events is scaffolded.
|
||||
signUpUseCase(
|
||||
ctx.container.get<IUsersRepository>(AUTH_SYMBOLS.IUsersRepository),
|
||||
ctx.container.get<IAuthenticationService>(AUTH_SYMBOLS.IAuthenticationService),
|
||||
new InMemoryEventBus(),
|
||||
undefined,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// packages/auth/src/events/user-signed-up.event.ts
|
||||
import { z } from "zod";
|
||||
import { defineEvent } from "@repo/core-events";
|
||||
|
||||
export const userSignedUpEventSchema = z
|
||||
.object({
|
||||
@@ -12,7 +11,9 @@ export const userSignedUpEventSchema = z
|
||||
|
||||
export type UserSignedUpEvent = z.infer<typeof userSignedUpEventSchema>;
|
||||
|
||||
export const userSignedUpEvent = defineEvent(
|
||||
"auth.user.signed-up",
|
||||
userSignedUpEventSchema,
|
||||
);
|
||||
// Inline event descriptor — core-events is optional. The shape { name, schema }
|
||||
// satisfies EventBusProtocol.publish / subscribe when the bus is present.
|
||||
export const userSignedUpEvent = {
|
||||
name: "auth.user.signed-up" as const,
|
||||
schema: userSignedUpEventSchema,
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/core-events": "workspace:*",
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"inversify": "^6.2.0",
|
||||
|
||||
@@ -111,64 +111,6 @@ export default [
|
||||
// Events + jobs rules are added here when @repo/core-events is scaffolded
|
||||
// via `pnpm turbo gen core-package events`.
|
||||
// <gen:events-rules>
|
||||
{
|
||||
files: ["**/*.{ts,tsx,mjs,cjs,js}"],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector:
|
||||
"ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"MemberExpression[object.type='MemberExpression'][object.object.type='Identifier'][object.object.name='payload'][object.property.type='Identifier'][object.property.name='jobs']",
|
||||
message:
|
||||
"Direct `payload.jobs.*` access is not allowed here. Use IJobQueue (from @repo/core-shared/jobs) instead. Allowed only in **/integrations/cms/jobs/** and **/core-shared/src/jobs/**.",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// J — `payload.jobs.*` is allowed only in the integration layer.
|
||||
// In these paths, no-restricted-syntax is narrowed to keep E1 active but
|
||||
// drop the payload.jobs check.
|
||||
// Note: "**/core-shared/src/jobs/**" does not match from within a package-local
|
||||
// ESLint run because ESLint resolves globs relative to the config file location.
|
||||
// The pattern is kept for documentation; in practice, the PayloadJobQueue class
|
||||
// uses `this.payload.jobs.*` which the selector already ignores (it only catches
|
||||
// bare `payload.jobs.*`). Any new file added there that does use bare `payload.jobs.*`
|
||||
// would need this allowlist to be expressed as "**/jobs/payload-*" or similar.
|
||||
{
|
||||
files: [
|
||||
"**/integrations/cms/jobs/**",
|
||||
"**/core-shared/src/jobs/**",
|
||||
],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector:
|
||||
"ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]",
|
||||
message:
|
||||
"Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// R2 / R1 (ADR-016) — realtime-specific ESLint rules (no-direct-socket-io,
|
||||
// no-realtime-handler-reexport) are added here when @repo/core-realtime is
|
||||
// scaffolded via `pnpm turbo gen core-package realtime`.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# @repo/core-events
|
||||
|
||||
Owns the cross-feature event bus: `IEventBus`, `defineEvent`, and two implementations (`InMemoryEventBus`, `PayloadJobsEventBus`).
|
||||
|
||||
**Boundary tag:** core. May be imported by feature, core, core-composition, app. May import from core-shared, tooling.
|
||||
|
||||
**Public surface:** `IEventBus`, `EventDescriptor`, `defineEvent`, `EventHandler`, `CORE_EVENTS_SYMBOLS`, both implementations.
|
||||
|
||||
**See:** `docs/decisions/adr-015-events-and-jobs.md` (pending), `docs/guides/events-and-jobs.md` (pending), `docs/superpowers/specs/2026-05-08-events-and-jobs-design.md`.
|
||||
@@ -1,3 +0,0 @@
|
||||
import baseConfig from "@repo/core-eslint/base";
|
||||
|
||||
export default baseConfig;
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "@repo/core-events",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit",
|
||||
"lint": "eslint .",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"zod": "^3.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"payload": { "optional": true }
|
||||
},
|
||||
"devDependencies": {
|
||||
"@repo/core-eslint": "workspace:*",
|
||||
"@repo/core-testing": "workspace:*",
|
||||
"@repo/core-typescript": "workspace:*",
|
||||
"typescript": "^5.8.0",
|
||||
"vitest": "^3.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { z } from "zod";
|
||||
import type { EventBusProtocol } from "@repo/core-shared/di/bind-protocols";
|
||||
import type { EventDescriptor } from "./event-descriptor";
|
||||
|
||||
export type EventHandler<T> = (event: T) => Promise<void>;
|
||||
|
||||
export interface IEventBus extends EventBusProtocol {
|
||||
publish<T>(
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
payload: T,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Subscribe a handler. `consumerFeature` is the kebab-case name of the
|
||||
* subscribing feature (e.g., "marketing-pages"). It is unused by
|
||||
* InMemoryEventBus; PayloadJobsEventBus uses it to name the fan-out task
|
||||
* slug deterministically (`__events.<event>.<consumerFeature>`).
|
||||
*/
|
||||
subscribe<T>(
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
consumerFeature: string,
|
||||
handler: EventHandler<T>,
|
||||
): void;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { z } from "zod";
|
||||
import { defineEvent } from "@/event-descriptor";
|
||||
|
||||
describe("defineEvent", () => {
|
||||
it("returns a descriptor with name and schema", () => {
|
||||
const schema = z.object({ id: z.string() }).strict();
|
||||
const descriptor = defineEvent("test.thing.happened", schema);
|
||||
expect(descriptor.name).toBe("test.thing.happened");
|
||||
expect(descriptor.schema).toBe(schema);
|
||||
});
|
||||
|
||||
it("descriptor.schema parses valid payloads", () => {
|
||||
const schema = z.object({ id: z.string() }).strict();
|
||||
const d = defineEvent("test.evt", schema);
|
||||
expect(() => d.schema.parse({ id: "abc" })).not.toThrow();
|
||||
});
|
||||
|
||||
it("descriptor.schema rejects invalid payloads", () => {
|
||||
const schema = z.object({ id: z.string() }).strict();
|
||||
const d = defineEvent("test.evt", schema);
|
||||
expect(() => d.schema.parse({ id: 123 })).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { z } from "zod";
|
||||
|
||||
export type EventDescriptor<TName extends string, TSchema extends z.ZodType> = {
|
||||
readonly name: TName;
|
||||
readonly schema: TSchema;
|
||||
};
|
||||
|
||||
export function defineEvent<TName extends string, TSchema extends z.ZodType>(
|
||||
name: TName,
|
||||
schema: TSchema,
|
||||
): EventDescriptor<TName, TSchema> {
|
||||
return { name, schema };
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
import { defineEvent } from "@/event-descriptor";
|
||||
import { InMemoryEventBus } from "@/in-memory-event-bus";
|
||||
|
||||
const evt = defineEvent("test.thing", z.object({ id: z.string() }).strict());
|
||||
|
||||
describe("InMemoryEventBus", () => {
|
||||
it("validates the payload via the descriptor's schema before fanout", async () => {
|
||||
const bus = new InMemoryEventBus();
|
||||
const handler = vi.fn();
|
||||
bus.subscribe(evt, "test-consumer", handler);
|
||||
await expect(bus.publish(evt, { id: 123 } as unknown as { id: string })).rejects.toThrow();
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("delivers to all registered handlers in parallel", async () => {
|
||||
const bus = new InMemoryEventBus();
|
||||
const a = vi.fn();
|
||||
const b = vi.fn();
|
||||
bus.subscribe(evt, "consumer-a", a);
|
||||
bus.subscribe(evt, "consumer-b", b);
|
||||
await bus.publish(evt, { id: "x" });
|
||||
expect(a).toHaveBeenCalledWith({ id: "x" });
|
||||
expect(b).toHaveBeenCalledWith({ id: "x" });
|
||||
});
|
||||
|
||||
it("swallows handler errors by default (publisher's publish does not throw)", async () => {
|
||||
const bus = new InMemoryEventBus();
|
||||
bus.subscribe(evt, "boom", async () => {
|
||||
throw new Error("subscriber blew up");
|
||||
});
|
||||
await expect(bus.publish(evt, { id: "x" })).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("rethrows the first handler error when failFast is true", async () => {
|
||||
const bus = new InMemoryEventBus({ failFast: true });
|
||||
bus.subscribe(evt, "first", async () => {
|
||||
throw new Error("first failure");
|
||||
});
|
||||
bus.subscribe(evt, "second", vi.fn());
|
||||
await expect(bus.publish(evt, { id: "x" })).rejects.toThrow("first failure");
|
||||
});
|
||||
|
||||
it("delivers nothing when no handlers are registered", async () => {
|
||||
const bus = new InMemoryEventBus();
|
||||
await expect(bus.publish(evt, { id: "x" })).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
import type { z } from "zod";
|
||||
import type { EventDescriptor } from "./event-descriptor";
|
||||
import type { EventHandler, IEventBus } from "./event-bus.interface";
|
||||
|
||||
export type InMemoryEventBusOptions = {
|
||||
/** When true, rethrow the first handler error (default: false — errors swallowed). */
|
||||
failFast?: boolean;
|
||||
};
|
||||
|
||||
export class InMemoryEventBus implements IEventBus {
|
||||
private readonly handlers = new Map<string, EventHandler<unknown>[]>();
|
||||
|
||||
constructor(private readonly options: InMemoryEventBusOptions = {}) {}
|
||||
|
||||
async publish<T>(
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
payload: T,
|
||||
): Promise<void> {
|
||||
descriptor.schema.parse(payload);
|
||||
const subscribers = this.handlers.get(descriptor.name) ?? [];
|
||||
if (subscribers.length === 0) return;
|
||||
const settled = await Promise.allSettled(
|
||||
subscribers.map((h) => h(payload)),
|
||||
);
|
||||
if (this.options.failFast) {
|
||||
const failure = settled.find((s) => s.status === "rejected");
|
||||
// Only the first rejection is rethrown. Other failures are intentionally
|
||||
// dropped — `failFast` is a test-affordance, not a fault-tolerance design.
|
||||
if (failure && failure.status === "rejected") throw failure.reason;
|
||||
}
|
||||
}
|
||||
|
||||
subscribe<T>(
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
_consumerFeature: string,
|
||||
handler: EventHandler<T>,
|
||||
): void {
|
||||
const arr = this.handlers.get(descriptor.name) ?? [];
|
||||
arr.push(handler as EventHandler<unknown>);
|
||||
this.handlers.set(descriptor.name, arr);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export type { EventDescriptor } from "./event-descriptor";
|
||||
export { defineEvent } from "./event-descriptor";
|
||||
export type { IEventBus, EventHandler } from "./event-bus.interface";
|
||||
export { CORE_EVENTS_SYMBOLS } from "./symbols";
|
||||
export { InMemoryEventBus, type InMemoryEventBusOptions } from "./in-memory-event-bus";
|
||||
export { PayloadJobsEventBus } from "./payload-jobs-event-bus";
|
||||
@@ -1,51 +0,0 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
import { defineEvent } from "@/event-descriptor";
|
||||
import { PayloadJobsEventBus } from "@/payload-jobs-event-bus";
|
||||
import type { IJobQueue } from "@repo/core-shared/jobs";
|
||||
|
||||
const evt = defineEvent("auth.user.signed-up", z.object({ userId: z.string() }).strict());
|
||||
|
||||
function recordingQueue(): IJobQueue & { enqueued: { taskSlug: string; input: unknown }[] } {
|
||||
const enqueued: { taskSlug: string; input: unknown }[] = [];
|
||||
const q: IJobQueue = {
|
||||
async enqueue(taskSlug, input) {
|
||||
enqueued.push({ taskSlug, input });
|
||||
return { jobId: `recording-${enqueued.length}` };
|
||||
},
|
||||
};
|
||||
return Object.assign(q, { enqueued });
|
||||
}
|
||||
|
||||
describe("PayloadJobsEventBus", () => {
|
||||
it("validates the payload before enqueueing", async () => {
|
||||
const queue = recordingQueue();
|
||||
const bus = new PayloadJobsEventBus(queue);
|
||||
bus.subscribe(evt, "marketing-pages", vi.fn());
|
||||
await expect(
|
||||
bus.publish(evt, { userId: 42 } as unknown as { userId: string }),
|
||||
).rejects.toThrow();
|
||||
expect(queue.enqueued).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("enqueues one task per subscriber, naming `__events.<event>.<consumer>`", async () => {
|
||||
const queue = recordingQueue();
|
||||
const bus = new PayloadJobsEventBus(queue);
|
||||
bus.subscribe(evt, "marketing-pages", vi.fn());
|
||||
bus.subscribe(evt, "blog", vi.fn());
|
||||
await bus.publish(evt, { userId: "u1" });
|
||||
expect(queue.enqueued).toHaveLength(2);
|
||||
expect(queue.enqueued.map((e) => e.taskSlug).sort()).toEqual([
|
||||
"__events.auth.user.signed-up.blog",
|
||||
"__events.auth.user.signed-up.marketing-pages",
|
||||
]);
|
||||
expect(queue.enqueued[0]!.input).toEqual({ userId: "u1" });
|
||||
});
|
||||
|
||||
it("enqueues nothing when no subscribers are registered", async () => {
|
||||
const queue = recordingQueue();
|
||||
const bus = new PayloadJobsEventBus(queue);
|
||||
await bus.publish(evt, { userId: "u1" });
|
||||
expect(queue.enqueued).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@@ -1,43 +0,0 @@
|
||||
import type { z } from "zod";
|
||||
import type { IJobQueue } from "@repo/core-shared/jobs";
|
||||
import type { EventDescriptor } from "./event-descriptor";
|
||||
import type { EventHandler, IEventBus } from "./event-bus.interface";
|
||||
|
||||
/**
|
||||
* Production-grade bus: for each subscriber, enqueues one Payload task per
|
||||
* `publish()` call. Subscribers register with their consumer-feature name so
|
||||
* fan-out tasks are named deterministically: `__events.<event>.<consumer>`.
|
||||
* The actual handler invocation happens inside Payload's job runner — see the
|
||||
* matching task config generated by `gen event consume` (Task 39).
|
||||
*/
|
||||
export class PayloadJobsEventBus implements IEventBus {
|
||||
private readonly subscribers = new Map<string, string[]>();
|
||||
|
||||
constructor(private readonly queue: IJobQueue) {}
|
||||
|
||||
async publish<T>(
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
payload: T,
|
||||
): Promise<void> {
|
||||
descriptor.schema.parse(payload);
|
||||
const consumers = this.subscribers.get(descriptor.name) ?? [];
|
||||
await Promise.all(
|
||||
consumers.map((consumerFeature) =>
|
||||
this.queue.enqueue(
|
||||
`__events.${descriptor.name}.${consumerFeature}`,
|
||||
payload,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
subscribe<T>(
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
consumerFeature: string,
|
||||
_handler: EventHandler<T>,
|
||||
): void {
|
||||
const arr = this.subscribers.get(descriptor.name) ?? [];
|
||||
if (!arr.includes(consumerFeature)) arr.push(consumerFeature);
|
||||
this.subscribers.set(descriptor.name, arr);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export const CORE_EVENTS_SYMBOLS = {
|
||||
IEventBus: Symbol.for("@repo/core-events/IEventBus"),
|
||||
} as const;
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": "@repo/core-typescript/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tags": ["core"]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import path from "node:path";
|
||||
import { mergeConfig } from "vitest/config";
|
||||
import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node";
|
||||
|
||||
export default mergeConfig(nodeVitestConfig, {
|
||||
resolve: {
|
||||
alias: { "@": path.resolve(__dirname, "./src") },
|
||||
},
|
||||
});
|
||||
@@ -23,7 +23,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/auth": "workspace:*",
|
||||
"@repo/core-events": "workspace:*",
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"inversify": "^6.2.0",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/core-events": "workspace:*",
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"inversify": "^6.2.0",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/core-events": "workspace:*",
|
||||
"@repo/core-shared": "workspace:*",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"inversify": "^6.2.0",
|
||||
|
||||
Reference in New Issue
Block a user