import type { CreateDraftResponse, HomeFocusRequest, HomeFocusResponse, ProjectActivityRequest, ProjectActivityResponse, UpdateProjectActivityRequest, ProjectEditResponse, ProjectIndexPage, ProjectUpdateRequest, PublishResponse, ReleaseEditResponse, ReleaseIndexPage, ReleaseUpdateRequest, TopicEdit, } from "../../contracts/management/contract.ts"; import type { ManagementGateway } from "../../application/ports/management-gateway.ts"; import { ManagementGatewayError } from "../../application/ports/management-gateway-error.ts"; import type { StudioOperationExecutor } from "./http-studio-gateway.ts"; export type { ManagementGateway }; const ROUTE_ID = "TECH_LOG_STUDIO"; /** * 주제·프로젝트 관리 게이트웨이. * *

Studio 게이트웨이와 같은 실패 규약을 쓴다 — 실패는 던지고, 화면은 `usePublicContent` 가 아니라 * Studio 쪽 상태 처리를 그대로 쓴다. 여기서 Result 로 감싸면 이 표면만 다른 규약이 된다. */ export { ManagementGatewayError, managementFailureMessage, } from "../../application/ports/management-gateway-error.ts"; export function createHttpManagementGateway( deps: Readonly<{ operations: StudioOperationExecutor }>, ): ManagementGateway { async function run(operationId: string, input: unknown): Promise { const outcome = await deps.operations.execute(operationId, input, { routeId: ROUTE_ID }); if (outcome.kind === "SUCCESS") return outcome.value as T; if (outcome.kind === "PROBLEM") { // The management surface answers with the ADR-006 envelope, which nests // the code under `error` — reading `problem.code` found nothing and every // failure surfaced as the literal "PROBLEM", matching no i18n key. const body = outcome.problem as | Readonly<{ code?: unknown; detail?: unknown; error?: Readonly<{ code?: unknown; message?: unknown }>; }> | null; const code = typeof body?.code === "string" ? body.code : typeof body?.error?.code === "string" ? body.error.code : "PROBLEM"; const detail = typeof body?.error?.message === "string" ? body.error.message : typeof body?.detail === "string" ? body.detail : ""; throw new ManagementGatewayError(operationId, code, detail); } throw new ManagementGatewayError(operationId, outcome.kind, ""); } return Object.freeze({ listTopics: () => run("listStudioTopics", {}), createTopic: (input: TopicEdit) => run("createTopic", input), updateTopic: (id: string, body: TopicEdit) => run("updateTopic", { id, body }), deleteTopic: async (id: string, expectedVersion: number) => { await run("deleteTopic", { id, expectedVersion }); }, listProjects: (page?: number, size?: number) => run("listStudioProjects", { ...(page !== undefined ? { page } : {}), ...(size !== undefined ? { size } : {}) }), getProject: (id: string) => run("getProjectForEdit", { id }), createProject: (title: string) => run("createProject", { title }), updateProject: (id: string, body: ProjectUpdateRequest) => run("updateProject", { id, body }), listReleases: (page?: number, size?: number) => run("listStudioReleases", { ...(page !== undefined ? { page } : {}), ...(size !== undefined ? { size } : {}) }), getRelease: (id: string) => run("getReleaseForEdit", { id }), createRelease: (title: string) => run("createRelease", { title }), updateRelease: (id: string, body: ReleaseUpdateRequest) => run("updateRelease", { id, body }), deleteRelease: async (id: string, expectedVersion: number) => { await run("deleteRelease", { id, expectedVersion }); }, listProjectActivities: (id: string) => run("listStudioProjectActivities", { id }), createProjectActivity: (id: string, body: ProjectActivityRequest) => run("createProjectActivity", { id, body }), updateProjectActivity: ( id: string, activityId: string, body: UpdateProjectActivityRequest, ) => run("updateProjectActivity", { id, activityId, body }), deleteProjectActivity: async (id: string, activityId: string, expectedVersion: number) => { await run("deleteProjectActivity", { id, activityId, expectedVersion }); }, publishProject: ( id: string, expectedVersion: number, visibility: "PUBLIC" | "UNLISTED" = "PUBLIC", ) => run("publishProject", { id, expectedVersion, visibility }), unpublishProject: (id: string, expectedVersion: number) => run("unpublishProject", { id, expectedVersion }), getHomeFocus: () => run("getHomeFocus", {}), updateHomeFocus: (body: HomeFocusRequest) => run("updateHomeFocus", body), publishRelease: (id: string, expectedVersion: number) => run("publishRelease", { id, expectedVersion }), archiveRelease: (id: string, expectedVersion: number) => run("archiveRelease", { id, expectedVersion }), deleteDocument: async ( kind: "CASE" | "REFERENCE" | "QUESTION", id: string, expectedVersion: number, ) => { const operationId = kind === "CASE" ? "deleteCaseDraft" : kind === "REFERENCE" ? "deleteReferenceDraft" : "deleteQuestion"; await run(operationId, { id, expectedVersion }); }, deleteDecision: async (projectId: string, decisionId: string, expectedVersion: number) => { await run("deleteProjectDecision", { id: projectId, decisionId, expectedVersion }); }, deleteProject: async (id: string, expectedVersion: number) => { await run("deleteProject", { id, expectedVersion }); }, }); }