chore: B1 모노레포 스캐폴드 스켈레톤 (yakcloud-packages)

- pnpm+turbo workspace, tsconfig base, .npmrc(Gitea 레지스트리)
- 7 패키지 스텁(ds-sdk 리프 → ui/api-client type-only → client-shell/bff-kit; backup-core 신규)
  package.json 의존으로 DAG·역의존 금지 규칙 강제
- Apache-2.0 LICENSE + NOTICE/LOGO_ATTRIBUTION(벤더 상표 로고 미번들, URL-only)
- .gitea/workflows/ci.yml(build/typecheck/lint · bind-env-drift · secret-scan · standalone-smoke)
- scripts: bind-env-check(check-only 게이트) · standalone-smoke(lazy-require 불변식) 스켈레톤
- apps/ds-web standalone 데모 스텁

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-25 11:02:21 +09:00
commit 4307e1ddb8
37 changed files with 579 additions and 0 deletions

51
.gitea/workflows/ci.yml Normal file
View File

@ -0,0 +1,51 @@
name: ci
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm typecheck
- run: pnpm lint
- run: pnpm test
bind-env-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# SSOT 게이트: ds-sdk bind-env.json ↔ private-cluster infra/api/yakcloud_api.py _bind_env_for()
# 리포 경계: 대상 Python 을 sparse-checkout(읽기전용 토큰)해 .infra-ref/ 로 가져온다.
- name: fetch infra ref (sparse)
run: |
echo "TODO: sparse-checkout private-cluster infra/api/yakcloud_api.py -> .infra-ref/"
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: node scripts/bind-env-check.mjs --python .infra-ref/infra/api/yakcloud_api.py
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: gitleaks
run: echo "TODO: gitleaks detect --no-git=false"
standalone-smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: pnpm install --frozen-lockfile
# #9 불변식: 무DB·무Keycloak 환경에서 클라 패키지 import 시 서버전용 모듈(Prisma/next-auth) 정적 유출 0
- run: node scripts/standalone-smoke.mjs

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
node_modules/
dist/
*.tsbuildinfo
.turbo/
.infra-ref/

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
# Gitea 단독 자체호스팅 레지스트리(확정). 실제 호스트로 교체.
@yakcloud:registry=https://gitea.yakenator.io/api/packages/yakcloud/npm/

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
[전체 Apache-2.0 본문은 https://www.apache.org/licenses/LICENSE-2.0.txt 에서
표준 텍스트로 채워 넣을 것 — 스캐폴드에는 헤더만 둔다.]
Copyright 2026 YakCloud
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

9
NOTICE Normal file
View File

@ -0,0 +1,9 @@
yakcloud-packages
Copyright 2026 YakCloud
이 저작물의 코드는 Apache License 2.0 으로 배포된다.
상표/로고 제외(TRADEMARKS): 본 저장소는 제3자 벤더의 상표·로고(MongoDB, Redis,
MinIO, MySQL, PostgreSQL, MariaDB, RabbitMQ, Apache Solr, Oracle 등)를 번들하지 않는다.
각 상표는 해당 소유자의 자산이며 Apache-2.0 라이선스 범위에 포함되지 않는다.
로고는 런타임에 URL(logoBase)로만 참조된다 — docs/LOGO_ATTRIBUTION.md 참조.

22
README.md Normal file
View File

@ -0,0 +1,22 @@
# yakcloud-packages
YakCloud 데이터소스 웹 클라이언트 공용 패키지 모노레포(오픈소스, Apache-2.0).
설계·판정 근거: private-cluster `docs/clients/{datasource-web-clients-design,phase-0-plan,scaffold-input,open-items-decisions}.md`.
## 패키지 의존 DAG (단방향 강제)
```
ds-sdk (리프: zod·DTO·BIND_ENV SSOT·ApiCode)
▲ type-only
├── api-client (fetch·usePoll; ⛔ 서버코드 유입 금지)
└── ui (react peer; ⛔ api-client 참조 금지)
└── client-shell (신규: 9종 데이터소스 웹 클라 셸)
auth-adapter (next-auth peer)
bff-kit (ds-sdk + auth-adapter; respond/session/serialize/ratelimit; ⛔ api-client 참조 금지)
backup-core (zod; 신규 전체)
```
역의존 금지 3규칙: ①ds-sdk 절대 리프(serialize는 bff-kit) ②respond=bff-kit 전용(공유는 ApiCode 리터럴만 ds-sdk) ③ui→콘솔 금지(help-sheets 분리).
## 상태
B1 스캐폴드 스켈레톤(빈 골격). 실제 추출(B3~B6)·MinIO(Phase1)·백업(M5)은 후속.

18
apps/ds-web/package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "ds-web",
"version": "0.0.0",
"private": true,
"description": "9종 데이터소스 통합 웹 클라 데모(standalone, 무DB). client-shell 소비.",
"type": "module",
"scripts": {
"build": "echo \"(ds-web build TODO: Next app)\"",
"typecheck": "tsc --noEmit",
"dev": "echo \"TODO\""
},
"dependencies": {
"@yakcloud/client-shell": "workspace:*",
"@yakcloud/ui": "workspace:*",
"@yakcloud/api-client": "workspace:*",
"@yakcloud/ds-sdk": "workspace:*"
}
}

3
apps/ds-web/src/main.ts Normal file
View File

@ -0,0 +1,3 @@
// ds-web — standalone(무DB) 데모 진입점 스텁.
import { __package } from "@yakcloud/client-shell";
console.log("ds-web demo →", __package);

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"noEmit": true
},
"include": [
"src"
]
}

19
docs/LOGO_ATTRIBUTION.md Normal file
View File

@ -0,0 +1,19 @@
# 로고/상표 귀속 (Logo Attribution)
**결정(2026-08-25):** 벤더 로고는 패키지에 **번들하지 않는다**. @yakcloud/ui 는 로고 **URL 경로만** export 하고
(`getServiceMeta(type, logoBase)`), 바이너리는 배포자가 제공한다(콘솔은 로컬 `public/logos/` 유지,
공개 배포는 Gitea Pages CDN, air-gap 은 `logoBase` 오버라이드).
| 데이터소스 | 상표 소유자 | 비고 |
|---|---|---|
| MongoDB | MongoDB, Inc. | 상표 가이드라인 준수 |
| Redis | Redis Ltd. | |
| MinIO | MinIO, Inc. | |
| MySQL | Oracle Corp. | |
| PostgreSQL | PostgreSQL Global Development Group | |
| MariaDB | MariaDB Foundation | |
| RabbitMQ | Broadcom/VMware | |
| Apache Solr | Apache Software Foundation | |
| Oracle | Oracle Corp. | oracle.png |
각 상표는 해당 소유자 자산. 본 저장소는 로고 바이너리를 재배포하지 않는다.

19
package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "yakcloud-packages",
"private": true,
"packageManager": "pnpm@9.12.0",
"engines": { "node": ">=20" },
"scripts": {
"build": "turbo run build",
"typecheck": "turbo run typecheck",
"lint": "turbo run lint",
"test": "turbo run test",
"bind-env:check": "node scripts/bind-env-check.mjs",
"smoke:standalone": "node scripts/standalone-smoke.mjs"
},
"devDependencies": {
"turbo": "^2.1.0",
"typescript": "^5.6.0",
"eslint": "^9.12.0"
}
}

View File

@ -0,0 +1,33 @@
{
"name": "@yakcloud/api-client",
"version": "0.0.0",
"description": "HTTP·폴링(클라 전용): apiGet/apiSend/apiDownload·usePoll. ⛔ 서버코드(respond) 유입 금지.",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"registry": "https://gitea.yakenator.io/api/packages/yakcloud/npm/"
},
"scripts": {
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"test": "echo \"(no tests yet)\""
},
"dependencies": {
"@yakcloud/ds-sdk": "workspace:*"
},
"peerDependencies": {
"react": ">=18"
}
}

View File

@ -0,0 +1,4 @@
// @yakcloud/api-client
// HTTP·폴링(클라 전용): apiGet/apiSend/apiDownload·usePoll. ⛔ 서버코드(respond) 유입 금지.
// B1 스켈레톤 — 실제 추출은 후속(scaffold-input.md 판정표 참조).
export const __package = "@yakcloud/api-client";

View File

@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src"
],
"references": [
{
"path": "../ds-sdk"
}
]
}

View File

@ -0,0 +1,30 @@
{
"name": "@yakcloud/auth-adapter",
"version": "0.0.0",
"description": "인증 어댑터(대부분 신규): IdP 인터페이스, auth.ts 씨앗. ⛔ 클라 패키지 정적 import 금지.",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"registry": "https://gitea.yakenator.io/api/packages/yakcloud/npm/"
},
"scripts": {
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"test": "echo \"(no tests yet)\""
},
"peerDependencies": {
"next-auth": ">=5"
}
}

View File

@ -0,0 +1,4 @@
// @yakcloud/auth-adapter
// 인증 어댑터(대부분 신규): IdP 인터페이스, auth.ts 씨앗. ⛔ 클라 패키지 정적 import 금지.
// B1 스켈레톤 — 실제 추출은 후속(scaffold-input.md 판정표 참조).
export const __package = "@yakcloud/auth-adapter";

View File

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src"
]
}

View File

@ -0,0 +1,30 @@
{
"name": "@yakcloud/backup-core",
"version": "0.0.0",
"description": "백업 계약(신규 전체): 백업대상·스케줄·restore-verification. v1 계약은 M5 랜딩(현재 빈 스켈레톤).",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"registry": "https://gitea.yakenator.io/api/packages/yakcloud/npm/"
},
"scripts": {
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"test": "echo \"(no tests yet)\""
},
"dependencies": {
"zod": "^3.23.0"
}
}

View File

@ -0,0 +1,4 @@
// @yakcloud/backup-core
// 백업 계약(신규 전체): 백업대상·스케줄·restore-verification. v1 계약은 M5 랜딩(현재 빈 스켈레톤).
// B1 스켈레톤 — 실제 추출은 후속(scaffold-input.md 판정표 참조).
export const __package = "@yakcloud/backup-core";

View File

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src"
]
}

View File

@ -0,0 +1,35 @@
{
"name": "@yakcloud/bff-kit",
"version": "0.0.0",
"description": "서버·BFF: respond/session/ratelimit/serialize/handlers. ⛔ api-client 참조 금지.",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"registry": "https://gitea.yakenator.io/api/packages/yakcloud/npm/"
},
"scripts": {
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"test": "echo \"(no tests yet)\""
},
"dependencies": {
"zod": "^3.23.0",
"@yakcloud/ds-sdk": "workspace:*",
"@yakcloud/auth-adapter": "workspace:*"
},
"peerDependencies": {
"next": ">=15"
}
}

View File

@ -0,0 +1,4 @@
// @yakcloud/bff-kit
// 서버·BFF: respond/session/ratelimit/serialize/handlers. ⛔ api-client 참조 금지.
// B1 스켈레톤 — 실제 추출은 후속(scaffold-input.md 판정표 참조).
export const __package = "@yakcloud/bff-kit";

View File

@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src"
],
"references": [
{
"path": "../ds-sdk"
},
{
"path": "../auth-adapter"
}
]
}

View File

@ -0,0 +1,35 @@
{
"name": "@yakcloud/client-shell",
"version": "0.0.0",
"description": "9종 데이터소스 웹 클라 셸(신규): 헤더/탭/pane/가드. FileBrowser 는 Phase 1.",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"registry": "https://gitea.yakenator.io/api/packages/yakcloud/npm/"
},
"scripts": {
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"test": "echo \"(no tests yet)\""
},
"dependencies": {
"@yakcloud/ui": "workspace:*",
"@yakcloud/api-client": "workspace:*",
"@yakcloud/ds-sdk": "workspace:*"
},
"peerDependencies": {
"react": ">=18"
}
}

View File

@ -0,0 +1,4 @@
// @yakcloud/client-shell
// 9종 데이터소스 웹 클라 셸(신규): 헤더/탭/pane/가드. FileBrowser 는 Phase 1.
// B1 스켈레톤 — 실제 추출은 후속(scaffold-input.md 판정표 참조).
export const __package = "@yakcloud/client-shell";

View File

@ -0,0 +1,21 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src"
],
"references": [
{
"path": "../ui"
},
{
"path": "../api-client"
},
{
"path": "../ds-sdk"
}
]
}

View File

@ -0,0 +1,30 @@
{
"name": "@yakcloud/ds-sdk",
"version": "0.0.0",
"description": "데이터소스 계약(리프): DTO·Zod 스키마·BIND_ENV SSOT·ApiCode 리터럴. ⛔ Prisma/react/서버 금지.",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"registry": "https://gitea.yakenator.io/api/packages/yakcloud/npm/"
},
"scripts": {
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"test": "echo \"(no tests yet)\""
},
"dependencies": {
"zod": "^3.23.0"
}
}

View File

@ -0,0 +1,4 @@
// @yakcloud/ds-sdk
// 데이터소스 계약(리프): DTO·Zod 스키마·BIND_ENV SSOT·ApiCode 리터럴. ⛔ Prisma/react/서버 금지.
// B1 스켈레톤 — 실제 추출은 후속(scaffold-input.md 판정표 참조).
export const __package = "@yakcloud/ds-sdk";

View File

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src"
]
}

34
packages/ui/package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "@yakcloud/ui",
"version": "0.0.0",
"description": "디자인시스템·프리미티브: 토큰·인라인SVG·CardSheet·LineChart(토큰색)·getServiceMeta(logoBase). ⛔ api-client 참조 금지.",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"publishConfig": {
"registry": "https://gitea.yakenator.io/api/packages/yakcloud/npm/"
},
"scripts": {
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"test": "echo \"(no tests yet)\""
},
"dependencies": {
"@yakcloud/ds-sdk": "workspace:*"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
}

4
packages/ui/src/index.ts Normal file
View File

@ -0,0 +1,4 @@
// @yakcloud/ui
// 디자인시스템·프리미티브: 토큰·인라인SVG·CardSheet·LineChart(토큰색)·getServiceMeta(logoBase). ⛔ api-client 참조 금지.
// B1 스켈레톤 — 실제 추출은 후속(scaffold-input.md 판정표 참조).
export const __package = "@yakcloud/ui";

15
packages/ui/tsconfig.json Normal file
View File

@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src"
],
"references": [
{
"path": "../ds-sdk"
}
]
}

3
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,3 @@
packages:
- "packages/*"
- "apps/*"

View File

@ -0,0 +1,14 @@
#!/usr/bin/env node
// bind-env SSOT check-only 게이트 (§4-F). 2축 대조:
// (a) 서비스별 <ALIAS>_* 환경변수 접미사 집합
// (b) URL 템플릿(스킴·쿼리키·특수플래그: authSource/JDBC thin/USE_SSL 등)
// 정본 = packages/ds-sdk/src/bind-env.json (9종: MONGODB..ORACLE)
// 대상 = private-cluster infra/api/yakcloud_api.py _bind_env_for() (sparse-checkout)
// 추가 리컨실: examples-data 커버리지(현재 8종, ORACLE 예제 부재) 경고.
// TODO(B3): ds-sdk/src/bind-env.json 확정 후 실제 파싱·대조 구현. 현재는 스켈레톤(경고 후 통과).
import { argv } from "node:process";
const pyIdx = argv.indexOf("--python");
const py = pyIdx >= 0 ? argv[pyIdx + 1] : null;
console.warn("[bind-env-check] SKELETON — ds-sdk/bind-env.json 미확정. python target:", py ?? "(none)");
console.warn("[bind-env-check] TODO: 접미사+URL템플릿 2축 대조 + examples-data(8종) 리컨실 구현(B3).");
process.exit(0);

View File

@ -0,0 +1,5 @@
#!/usr/bin/env node
// #9 lazy-require 불변식 스모크: 무DB·무Keycloak 에서 클라 패키지가 서버전용 모듈을 정적 import 하지 않는지.
// TODO(B3): @yakcloud/ui·api-client·ds-sdk 를 DATABASE_URL·KEYCLOAK 미설정으로 import 해 throw 0 확인.
console.warn("[standalone-smoke] SKELETON — 패키지 추출(B3) 후 실제 import 스모크 구현.");
process.exit(0);

17
tsconfig.base.json Normal file
View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"strict": true,
"declaration": true,
"declarationMap": true,
"composite": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"verbatimModuleSyntax": true,
"noUncheckedIndexedAccess": true
}
}

9
turbo.json Normal file
View File

@ -0,0 +1,9 @@
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
"typecheck": { "dependsOn": ["^build"] },
"lint": {},
"test": { "dependsOn": ["^build"] }
}
}