feat: 프로젝트를 공개할 수 있게 하고, 홈이 무엇을 앞에 둘지 고를 수 있게 한다
공개 화면 다섯 곳이 조용히 비어 있었다. 원인은 하나씩 달랐지만 모두 "값을 채울 방법이 없었다"는 같은 모양이었다. 홈의 "지금 집중하는 것" — `home_focus_config` 는 마이그레이션이 빈 행 하나만 넣어 두었고, 계약에 선언된 `getHomeFocus`/`updateHomeFocus` 는 구현이 없었다. 세 슬롯이 모두 비면 홈은 그 영역을 아예 그리지 않으므로, 운영에서는 한 번도 나타난 적이 없다. Studio 대시보드에 고르는 화면을 둔다. 홈의 "최근 기록" — 화면이 공개된 프로젝트를 하나씩 돌며 타임라인을 조립했다. 그래서 게시한 문서라도 그 프로젝트가 공개되어 있지 않으면 목록에서 통째로 빠졌고, 실제로 릴리스 한 줄만 남았다. 무엇이 최근인지는 공개 투영이 이미 알고 있으므로 그것을 그대로 읽는다. 프로젝트마다 요청을 보내던 N+1 도 사라진다. 프로젝트 공개 — 프로젝트는 `RecordKind` 에 없어 문서 게시 파이프라인을 타지 못하는데, 공개 화면들(프로젝트 목록·프로필의 "현재 프로젝트"·홈 focus)은 전부 `public_resource_projection` 의 PROJECT 행을 가시성 관문으로 쓴다. 그 행을 세우는 경로가 없었으므로 프로젝트는 영원히 비공개였다. 계약에 이미 있던 `publishProject`/`unpublishProject` 를 구현하고 주제·프로젝트 화면에 버튼을 둔다. 문서 사이 관계 연결 — `JdbcCatalogQueryAdapter` 의 RELATION/EVIDENCE 가 `List.of()` 스텁이라 어떤 기록도 연결 대상 목록을 채울 수 없었다. RELATION 은 작성 중에 고르는 것이므로 작업본까지 포함하고, EVIDENCE 는 읽는 사람이 따라갈 수 있어야 하므로 공개된 것만 포함한다. 본문 너비 — 문서 한 편이 세 폭으로 갈라져 있었다. 머리말 920px, 유형·프로젝트 줄은 shell 전체 1180px, 본문은 672px 를 가운데 정렬. 셋을 같은 폭·같은 왼쪽 끝에 세우고 읽는 단을 56rem 으로 넓힌다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XEHXspz4rv5pB5wiiSsVDu
This commit is contained in:
co-authored by
Claude Opus 5
parent
c03b0c77b8
commit
b3aa304975
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"packageId": "@tech-log/management-contract",
|
||||
"version": "1.0.0",
|
||||
"digest": "sha256:d19ae7c4fbcac924a356bbb0cc1a46a4046ecec701158ca0a9b7cc089bbaf878",
|
||||
"digest": "sha256:446b14486291ab89b5b42d86aea1c1c7007ac40583aa98fe50d6e741d61aa1b3",
|
||||
"sourceRevision": "b195b29",
|
||||
"operationIds": [
|
||||
"createCaseDraft",
|
||||
|
||||
@@ -15,3 +15,5 @@ export type ReleaseIndexItem = Schemas["ReleaseIndexItem"];
|
||||
export type ReleaseIndexPage = Schemas["ReleaseIndexPage"];
|
||||
export type ReleaseUpdateRequest = Schemas["ReleaseUpdateRequest"];
|
||||
export type PublishResponse = Schemas["PublishResponse"];
|
||||
export type HomeFocusRequest = Schemas["HomeFocusRequest"];
|
||||
export type HomeFocusResponse = Schemas["HomeFocusResponse"];
|
||||
|
||||
@@ -963,6 +963,12 @@ export interface components {
|
||||
data: components["schemas"]["ReleaseEditResponse"];
|
||||
meta: components["schemas"]["ResponseMeta"];
|
||||
};
|
||||
HomeFocusResponseEnvelope: {
|
||||
/** @constant */
|
||||
success: true;
|
||||
data: components["schemas"]["HomeFocusResponse"];
|
||||
meta: components["schemas"]["ResponseMeta"];
|
||||
};
|
||||
PublishResponseEnvelope: {
|
||||
/** @constant */
|
||||
success: true;
|
||||
@@ -5519,7 +5525,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["PublishResponse"];
|
||||
"application/json": components["schemas"]["PublishResponseEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Bad Request */
|
||||
@@ -5528,7 +5534,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
@@ -5537,7 +5543,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
@@ -5546,7 +5552,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
@@ -5555,7 +5561,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Conflict */
|
||||
@@ -5564,7 +5570,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unprocessable Content */
|
||||
@@ -5573,7 +5579,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Internal Server Error */
|
||||
@@ -5582,7 +5588,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -5610,7 +5616,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ProjectEditResponse"];
|
||||
"application/json": components["schemas"]["ProjectEditResponseEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Bad Request */
|
||||
@@ -5619,7 +5625,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
@@ -5628,7 +5634,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
@@ -5637,7 +5643,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
@@ -5646,7 +5652,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Conflict */
|
||||
@@ -5655,7 +5661,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unprocessable Content */
|
||||
@@ -5664,7 +5670,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Internal Server Error */
|
||||
@@ -5673,7 +5679,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -8117,7 +8123,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HomeFocusResponse"];
|
||||
"application/json": components["schemas"]["HomeFocusResponseEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Bad Request */
|
||||
@@ -8126,7 +8132,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
@@ -8135,7 +8141,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
@@ -8144,7 +8150,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
@@ -8153,7 +8159,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Internal Server Error */
|
||||
@@ -8162,7 +8168,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -8188,7 +8194,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HomeFocusResponse"];
|
||||
"application/json": components["schemas"]["HomeFocusResponseEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Bad Request */
|
||||
@@ -8197,7 +8203,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
@@ -8206,7 +8212,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
@@ -8215,7 +8221,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
@@ -8224,7 +8230,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Conflict */
|
||||
@@ -8233,7 +8239,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unprocessable Content */
|
||||
@@ -8242,7 +8248,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Internal Server Error */
|
||||
@@ -8251,7 +8257,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/problem+json": components["schemas"]["ProblemDetails"];
|
||||
"application/json": components["schemas"]["ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3015,49 +3015,49 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PublishResponse'
|
||||
$ref: '#/components/schemas/PublishResponseEnvelope'
|
||||
'400':
|
||||
description: Bad Request
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'403':
|
||||
description: Forbidden
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'404':
|
||||
description: Not Found
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'409':
|
||||
description: Conflict
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'422':
|
||||
description: Unprocessable Content
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'500':
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -3085,49 +3085,49 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProjectEditResponse'
|
||||
$ref: '#/components/schemas/ProjectEditResponseEnvelope'
|
||||
'400':
|
||||
description: Bad Request
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'403':
|
||||
description: Forbidden
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'404':
|
||||
description: Not Found
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'409':
|
||||
description: Conflict
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'422':
|
||||
description: Unprocessable Content
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'500':
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -4985,37 +4985,37 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HomeFocusResponse'
|
||||
$ref: '#/components/schemas/HomeFocusResponseEnvelope'
|
||||
'400':
|
||||
description: Bad Request
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'403':
|
||||
description: Forbidden
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'404':
|
||||
description: Not Found
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'500':
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
security:
|
||||
- sessionCookie: []
|
||||
put:
|
||||
@@ -5030,49 +5030,49 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HomeFocusResponse'
|
||||
$ref: '#/components/schemas/HomeFocusResponseEnvelope'
|
||||
'400':
|
||||
description: Bad Request
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'403':
|
||||
description: Forbidden
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'404':
|
||||
description: Not Found
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'409':
|
||||
description: Conflict
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'422':
|
||||
description: Unprocessable Content
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'500':
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/problem+json:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -5510,6 +5510,21 @@ components:
|
||||
$ref: '#/components/schemas/ReleaseEditResponse'
|
||||
meta:
|
||||
$ref: '#/components/schemas/ResponseMeta'
|
||||
HomeFocusResponseEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- success
|
||||
- data
|
||||
- meta
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
const: true
|
||||
data:
|
||||
$ref: '#/components/schemas/HomeFocusResponse'
|
||||
meta:
|
||||
$ref: '#/components/schemas/ResponseMeta'
|
||||
PublishResponseEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"packageId": "@tech-log/studio-contract",
|
||||
"version": "3.0.0",
|
||||
"digest": "sha256:674327a82951fd4a1bc2594c858072dfcd0b9abe7c63198283da5d8c92a04326",
|
||||
"version": "3.1.0",
|
||||
"digest": "sha256:6fc015ca6727af88b7fb0088e02ba97846e1dd79fb0d4fc593cc79f2a3b9795f",
|
||||
"sourceRevision": "b195b29",
|
||||
"operationIds": [
|
||||
"getStudioSession",
|
||||
|
||||
@@ -1074,7 +1074,32 @@ export interface components {
|
||||
height: number | null;
|
||||
decorative: boolean;
|
||||
};
|
||||
CaseRenderBlock: components["schemas"]["HeadingBlock"] | components["schemas"]["ParagraphBlock"] | components["schemas"]["BlockquoteBlock"] | components["schemas"]["UnorderedListBlock"] | components["schemas"]["OrderedListBlock"] | components["schemas"]["CodeBlock"] | components["schemas"]["DataTableBlock"] | components["schemas"]["CalloutBlock"] | components["schemas"]["EvidenceFigureBlock"];
|
||||
/** @description `---` 로 쓴 구분선이다. 담을 내용이 없으므로 `type` 뿐이다.
|
||||
* */
|
||||
ThematicBreakBlock: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
type: "THEMATIC_BREAK";
|
||||
};
|
||||
/** @description `` 로 쓴 그림이다.
|
||||
*
|
||||
* `EvidenceFigureBlock` 과 나누는 기준은 출처다. evidence 는 assetKey 로 가리켜 게시
|
||||
* 시점에 고정되고 확대 보기를 갖지만, 이쪽은 작성자가 적은 경로를 그대로 쓴다. 경로 규칙은
|
||||
* 링크와 같다 — 외부 스킴과 `javascript:` 는 거절한다.
|
||||
* */
|
||||
ImageBlock: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
type: "IMAGE";
|
||||
src: string;
|
||||
alt: string;
|
||||
title: string | null;
|
||||
};
|
||||
CaseRenderBlock: components["schemas"]["HeadingBlock"] | components["schemas"]["ParagraphBlock"] | components["schemas"]["BlockquoteBlock"] | components["schemas"]["UnorderedListBlock"] | components["schemas"]["OrderedListBlock"] | components["schemas"]["CodeBlock"] | components["schemas"]["DataTableBlock"] | components["schemas"]["CalloutBlock"] | components["schemas"]["EvidenceFigureBlock"] | components["schemas"]["ThematicBreakBlock"] | components["schemas"]["ImageBlock"];
|
||||
CasePublicRenderModel: components["schemas"]["PublicRenderModelBase"] & {
|
||||
/** @enum {string} */
|
||||
kind: "CASE";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Tech Log Studio API
|
||||
version: 3.0.0
|
||||
version: 3.1.0
|
||||
description: |
|
||||
Tech Log Studio orchestration 계약이다.
|
||||
|
||||
@@ -1321,7 +1321,10 @@ components:
|
||||
properties:
|
||||
type: { type: string, enum: [HEADING] }
|
||||
id: { type: string, minLength: 1, maxLength: 200 }
|
||||
level: { type: integer, minimum: 2, maximum: 4 }
|
||||
# 작성자가 쓴 그대로 담는다. 서버 렌더러는 이 값을 2..4 로 좁혀 문서 안 제목 위계를
|
||||
# 지키므로(BlockRenderer), 계약이 1..6 을 거절할 이유가 없다 — 거절하면 `#` 로 시작한
|
||||
# 평범한 Markdown 이 통째로 렌더링되지 않는다.
|
||||
level: { type: integer, minimum: 1, maximum: 6 }
|
||||
content: { type: array, maxItems: 1000, items: { $ref: "#/components/schemas/Inline" } }
|
||||
ParagraphBlock:
|
||||
type: object
|
||||
@@ -1452,6 +1455,29 @@ components:
|
||||
width: { type: [integer, "null"], minimum: 1 }
|
||||
height: { type: [integer, "null"], minimum: 1 }
|
||||
decorative: { type: boolean }
|
||||
ThematicBreakBlock:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: |
|
||||
`---` 로 쓴 구분선이다. 담을 내용이 없으므로 `type` 뿐이다.
|
||||
required: [type]
|
||||
properties:
|
||||
type: { type: string, enum: [THEMATIC_BREAK] }
|
||||
ImageBlock:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: |
|
||||
`` 로 쓴 그림이다.
|
||||
|
||||
`EvidenceFigureBlock` 과 나누는 기준은 출처다. evidence 는 assetKey 로 가리켜 게시
|
||||
시점에 고정되고 확대 보기를 갖지만, 이쪽은 작성자가 적은 경로를 그대로 쓴다. 경로 규칙은
|
||||
링크와 같다 — 외부 스킴과 `javascript:` 는 거절한다.
|
||||
required: [type, src, alt, title]
|
||||
properties:
|
||||
type: { type: string, enum: [IMAGE] }
|
||||
src: { type: string, minLength: 1, maxLength: 500 }
|
||||
alt: { type: string, maxLength: 300 }
|
||||
title: { type: [string, "null"], maxLength: 300 }
|
||||
CaseRenderBlock:
|
||||
oneOf:
|
||||
- { $ref: "#/components/schemas/HeadingBlock" }
|
||||
@@ -1463,6 +1489,8 @@ components:
|
||||
- { $ref: "#/components/schemas/DataTableBlock" }
|
||||
- { $ref: "#/components/schemas/CalloutBlock" }
|
||||
- { $ref: "#/components/schemas/EvidenceFigureBlock" }
|
||||
- { $ref: "#/components/schemas/ThematicBreakBlock" }
|
||||
- { $ref: "#/components/schemas/ImageBlock" }
|
||||
discriminator:
|
||||
propertyName: type
|
||||
mapping:
|
||||
@@ -1475,6 +1503,8 @@ components:
|
||||
DATA_TABLE: "#/components/schemas/DataTableBlock"
|
||||
CALLOUT: "#/components/schemas/CalloutBlock"
|
||||
EVIDENCE_FIGURE: "#/components/schemas/EvidenceFigureBlock"
|
||||
THEMATIC_BREAK: "#/components/schemas/ThematicBreakBlock"
|
||||
IMAGE: "#/components/schemas/ImageBlock"
|
||||
CasePublicRenderModel:
|
||||
unevaluatedProperties: false
|
||||
allOf:
|
||||
|
||||
@@ -310,6 +310,47 @@ const HTTP_CONTRACTS = Object.freeze([
|
||||
});
|
||||
},
|
||||
),
|
||||
writeOperation(
|
||||
"publishProject",
|
||||
"POST",
|
||||
`${P}/{id}/publish`,
|
||||
{ acceptedStatuses: [200], requestByteLimit: 1_024, responseByteLimit: 8_192 },
|
||||
(input: never) => {
|
||||
const value = input as unknown as Readonly<{
|
||||
id: string;
|
||||
expectedVersion: number;
|
||||
visibility: "PUBLIC" | "UNLISTED";
|
||||
}>;
|
||||
return Object.freeze({
|
||||
pathValues: Object.freeze({ id: value.id }),
|
||||
queryEntries: NO_QUERY,
|
||||
body: { expectedVersion: value.expectedVersion, visibility: value.visibility },
|
||||
});
|
||||
},
|
||||
),
|
||||
writeOperation(
|
||||
"unpublishProject",
|
||||
"POST",
|
||||
`${P}/{id}/unpublish`,
|
||||
{ acceptedStatuses: [200], requestByteLimit: 1_024, responseByteLimit: 262_144 },
|
||||
(input: never) => {
|
||||
const value = input as unknown as Readonly<{ id: string; expectedVersion: number }>;
|
||||
return Object.freeze({
|
||||
pathValues: Object.freeze({ id: value.id }),
|
||||
queryEntries: NO_QUERY,
|
||||
body: { expectedVersion: value.expectedVersion },
|
||||
});
|
||||
},
|
||||
),
|
||||
readOperation("getHomeFocus", `${D}/home-focus`, 8_192),
|
||||
writeOperation(
|
||||
"updateHomeFocus",
|
||||
"PUT",
|
||||
`${D}/home-focus`,
|
||||
{ acceptedStatuses: [200], requestByteLimit: 4_096, responseByteLimit: 8_192 },
|
||||
(input: never) =>
|
||||
Object.freeze({ pathValues: NO_PATH, queryEntries: NO_QUERY, body: input }),
|
||||
),
|
||||
writeOperation(
|
||||
"publishRelease",
|
||||
"POST",
|
||||
|
||||
Reference in New Issue
Block a user