build: generate the TechLog Studio contract from canonical source
Vendors the canonical studio-v1.yaml, generates types via an isolated `pnpm dlx` toolchain (openapi-typescript needs TypeScript 5's classic compiler API; this repo pins TypeScript 7.0.2 per VD-01, whose root export has none), and adds an offline drift gate that checks the vendored yaml/generated types/canonical-source.json against each other without touching the sibling design-package repo or the network. Regenerating from canonical surfaces real, new required fields on existing schemas (WorkingCopyDetail.nextAction, PreviewDetail/PublicPreview .dependencyRevision, StudioDashboard.totals.needsValidation, PublicationSnapshot.contentFormatVersion/rendererContractVersion) and a new required EvidenceFigureBlock.asset. The mock gateway and fixtures are updated to satisfy the former; the latter exposes a real authoring- vs-rendering conflation in the content-format parser (it declared its output as the server's fully-resolved PublicRenderModel type, which it has no asset catalog to satisfy). Split that boundary: the parser now produces an authoring block type omitting the resolved asset, and each of its three consumers (the mock gateway, the Studio instant preview, and the static Case demo page) attaches the resolved descriptor from its own asset source through a shared, pure domain-level resolver. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ac555a85e8
commit
639e1a49c9
@@ -1,6 +1,10 @@
|
||||
import type { CaseRecord } from "../../../application/ports/public-content-queries.ts";
|
||||
import type { components } from "../../../contracts/studio/generated.ts";
|
||||
import { parseCaseContent } from "../../../domain/content-format/parse-case-content.ts";
|
||||
import {
|
||||
parseCaseContent,
|
||||
type CaseAuthoringBlock,
|
||||
} from "../../../domain/content-format/parse-case-content.ts";
|
||||
import { resolveCaseEvidenceAssets } from "../../../domain/content-format/project-public-render-model.ts";
|
||||
import type { EvidenceAsset } from "../../../domain/public-render-content.ts";
|
||||
import { PublicRecordRenderer } from "../../shared/public-render/public-record-renderer.tsx";
|
||||
import { publicRenderModelBase } from "./public-document-header.tsx";
|
||||
@@ -113,9 +117,7 @@ Batch Fetch는 컬렉션 N+1을 줄이지만 필요한 자식만 자동으로
|
||||
|
||||
> 컬렉션이 포함된 목록에서는 부모 페이지를 먼저 데이터베이스에서 고정한다. 연관 데이터는 현재 페이지의 부모 키로 제한해 별도 조회한다. 반환 개수만 보지 말고 SQL LIMIT, 전송 행, 로드 엔티티를 각각 측정한다.`;
|
||||
|
||||
function genericCaseBlocks(
|
||||
record: CaseRecord,
|
||||
): components["schemas"]["CaseRenderBlock"][] {
|
||||
function genericCaseBlocks(record: CaseRecord): CaseAuthoringBlock[] {
|
||||
return record.sections.flatMap((section) => {
|
||||
const blocks: components["schemas"]["CaseRenderBlock"][] = [
|
||||
{
|
||||
@@ -157,20 +159,44 @@ function resolvePublicEvidenceAsset(key: string): EvidenceAsset {
|
||||
};
|
||||
}
|
||||
|
||||
export function CaseDocumentPage({ record }: { record: CaseRecord }) {
|
||||
const model: components["schemas"]["CasePublicRenderModel"] = {
|
||||
...publicRenderModelBase(record),
|
||||
kind: "CASE",
|
||||
problem: record.problem,
|
||||
conclusion: record.conclusion,
|
||||
environment: record.environment,
|
||||
reproduction: record.verification,
|
||||
lastVerifiedOn: record.lastVerifiedLabel.replaceAll(".", "-"),
|
||||
bodyBlocks:
|
||||
record.slug === "collection-fetch-join-pagination"
|
||||
? parseCaseContent(fetchJoinBody)
|
||||
: genericCaseBlocks(record),
|
||||
// This page has no adapter-level asset catalog access, so it resolves the
|
||||
// canonical descriptor from the same local fixture data as `resolvePublicEvidenceAsset`
|
||||
// above. The `assetId` is a fixed literal (not derived) because this file only ever
|
||||
// resolves this one key; it only needs to be stable, not computed.
|
||||
function resolvePublicEvidenceAssetDescriptor(
|
||||
key: string,
|
||||
): components["schemas"]["ResolvedAsset"] {
|
||||
if (key !== "fetch-strategy-boundary") {
|
||||
throw new Error(`Unknown local evidence asset: ${key}`);
|
||||
}
|
||||
return {
|
||||
assetId: "00000000-0000-4000-8000-000000000001",
|
||||
assetKey: key,
|
||||
mediaType: "image/svg+xml",
|
||||
publicPath: "/media/fetch-strategy-boundary.svg",
|
||||
width: 1080,
|
||||
height: 420,
|
||||
decorative: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function CaseDocumentPage({ record }: { record: CaseRecord }) {
|
||||
const model = resolveCaseEvidenceAssets(
|
||||
{
|
||||
...publicRenderModelBase(record),
|
||||
kind: "CASE",
|
||||
problem: record.problem,
|
||||
conclusion: record.conclusion,
|
||||
environment: record.environment,
|
||||
reproduction: record.verification,
|
||||
lastVerifiedOn: record.lastVerifiedLabel.replaceAll(".", "-"),
|
||||
bodyBlocks:
|
||||
record.slug === "collection-fetch-join-pagination"
|
||||
? parseCaseContent(fetchJoinBody)
|
||||
: genericCaseBlocks(record),
|
||||
},
|
||||
resolvePublicEvidenceAssetDescriptor,
|
||||
);
|
||||
|
||||
return (
|
||||
<PublicRecordRenderer
|
||||
|
||||
Reference in New Issue
Block a user