Initial commit

This commit is contained in:
fraqtal
2026-07-12 08:15:46 +00:00
commit ee0fec0691
1397 changed files with 127242 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
import { t } from "@repo/core-shared/trpc/init";
import { defineErrorMiddleware } from "@repo/core-shared/trpc/define-error-middleware";
import { HeaderNotFoundError } from "../../entities/errors/header";
import { InputParseError } from "../../entities/errors/common";
export const navigationProcedure = t.procedure.use(
defineErrorMiddleware([
[InputParseError, "BAD_REQUEST"],
[HeaderNotFoundError, "NOT_FOUND"],
]),
);

View File

@@ -0,0 +1,93 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { TRPCError } from "@trpc/server";
import { injectable } from "inversify";
import { navigationContainer } from "@/di/container";
import { NavigationModule } from "@/di/module";
import { NAVIGATION_SYMBOLS } from "@/di/symbols";
import { getHeaderUseCase } from "@/application/use-cases/get-header.use-case";
import { getHeaderController } from "@/interface-adapters/controllers/get-header.controller";
import { navigationRouter } from "@/integrations/api/router";
describe("navigationRouter", () => {
beforeEach(() => {
navigationContainer.unbindAll();
navigationContainer.load(NavigationModule);
});
afterEach(() => {
navigationContainer.unbindAll();
});
it("exposes header procedure", () => {
const names = Object.keys(navigationRouter._def.procedures);
expect(names).toContain("header");
});
it("header returns 3 items", async () => {
const caller = navigationRouter.createCaller({});
const result = await caller.header({});
expect(result.items).toHaveLength(3);
});
});
describe("navigationRouter error mapping", () => {
beforeEach(() => {
navigationContainer.unbindAll();
navigationContainer.load(NavigationModule);
});
afterEach(() => {
navigationContainer.unbindAll();
});
it("translates InputParseError → BAD_REQUEST when extra fields are passed", async () => {
const caller = navigationRouter.createCaller({});
try {
await caller.header({ unexpected: "field" } as unknown as Record<
string,
never
>);
throw new Error("expected throw");
} catch (e) {
expect(e).toBeInstanceOf(TRPCError);
expect((e as TRPCError).code).toBe("BAD_REQUEST");
}
});
it("translates HeaderNotFoundError → NOT_FOUND when repository returns null", async () => {
@injectable()
class NullHeaderRepository {
async getHeader() {
return null as never;
}
}
navigationContainer.unbindAll();
navigationContainer
.bind(NAVIGATION_SYMBOLS.IHeaderRepository)
.to(NullHeaderRepository);
navigationContainer
.bind(NAVIGATION_SYMBOLS.IGetHeaderUseCase)
.toDynamicValue((ctx) =>
getHeaderUseCase(
ctx.container.get(NAVIGATION_SYMBOLS.IHeaderRepository),
),
);
navigationContainer
.bind(NAVIGATION_SYMBOLS.IGetHeaderController)
.toDynamicValue((ctx) =>
getHeaderController(
ctx.container.get(NAVIGATION_SYMBOLS.IGetHeaderUseCase),
),
);
const caller = navigationRouter.createCaller({});
try {
await caller.header({});
throw new Error("expected throw");
} catch (e) {
expect(e).toBeInstanceOf(TRPCError);
expect((e as TRPCError).code).toBe("NOT_FOUND");
}
});
});

View File

@@ -0,0 +1,22 @@
import { router } from "@repo/core-shared/trpc/init";
import { navigationContainer } from "../../di/container";
import { NAVIGATION_SYMBOLS } from "../../di/symbols";
import { getHeaderInputSchema } from "../../application/use-cases/get-header.use-case";
import type { IGetHeaderController } from "../../interface-adapters/controllers/get-header.controller";
import { navigationProcedure } from "./procedures";
export const navigationRouter = router({
header: navigationProcedure
.input(getHeaderInputSchema)
.query(({ input }) => {
const ctrl = navigationContainer.get<IGetHeaderController>(
NAVIGATION_SYMBOLS.IGetHeaderController,
);
return ctrl(input);
}),
});
export type NavigationRouter = typeof navigationRouter;

View File

@@ -0,0 +1,34 @@
import type { GlobalConfig } from "payload";
export const header: GlobalConfig = {
slug: "header",
custom: {
retention: {
purgeSchedule: "monthly",
postDeletion: {
duration: "P90D",
trigger: "after-deletion",
action: "hard-delete",
},
},
},
admin: {
group: "Navigation",
},
fields: [
{
name: "logo",
type: "upload",
relationTo: "media",
},
{
name: "items",
type: "array",
fields: [
{ name: "label", type: "text", required: true },
{ name: "href", type: "text", required: true },
{ name: "external", type: "checkbox", defaultValue: false },
],
},
],
};

View File

@@ -0,0 +1,2 @@
export { header } from "./globals/header";
// <gen:job-tasks>