# dwg-dxf-viewer-sample

DWG/DXF 2D viewer module과 다중 포맷 3D viewer sample을 함께 관리하는
Three.js 기반 npm workspace입니다.

현재 구현은 파일을 브라우저에서 파싱하고 화면에 표시하는 기술 검증용입니다.
좌표, 단위, 원점, 형상 허용오차, topology와 CAD/BIM 의미 정보의 보존을
보장하는 운영용 engineering viewer로는 아직 검증되지 않았습니다.

## 개발 스택 및 구성 정책

이 저장소의 표준 개발 대상은 **Linux x86_64 container**입니다. Windows host에서는
Docker Desktop 또는 WSL2를 container runtime 진입점으로만 사용하며, Windows drive
path와 host의 `node_modules`를 표준 build 입력으로 사용하지 않습니다.

| 구분 | 표준 stack | 구성 정책 |
|---|---|---|
| Host·container | Linux x86_64, glibc 기반 image | Chrome과 native npm package 호환성이 확인될 때까지 Debian/Ubuntu 계열을 기준으로 하며 Alpine/musl은 별도 검증 없이 사용하지 않음 |
| JavaScript runtime | Node.js 24 이상, npm 11 이상 | `package.json#engines`가 지원 하한이며 container와 CI에서는 24.x·11.x의 exact version 및 base image digest를 고정 |
| Package 관리 | npm workspaces, `package-lock.json` | clean environment는 `npm ci`만 사용하고 lockfile을 canonical dependency graph로 취급하며 host `node_modules`를 container에 mount하지 않음 |
| Application | Vite 8, TypeScript 7, Three.js r185 | `apps/*`는 실행 application, `packages/*`는 workspace 내부 module로 유지하며 browser runtime asset은 Vite `BASE_URL`을 따름 |
| Unit·contract test | Vitest 4 | root `tests/**/*.test.ts`를 기준으로 하고 변경 시 관련 test부터 실행한 뒤 전체 suite를 실행 |
| Browser E2E | Playwright 1.62, system Chrome/Chromium | browser binary와 Linux system library를 E2E image에 고정하고 `PLAYWRIGHT_CHROMIUM_EXECUTABLE`로 경로를 주입 |
| Runtime decoder | acadrust DWG WASM, Draco, Basis, web-ifc | `npm ci`의 `postinstall`로 설치 dependency와 version이 맞는 artifact를 staging하며 수동 복사를 canonical 절차로 사용하지 않음 |
| Optional asset tool | Puppeteer Core, ffmpeg, Blender, `toktx` | application build의 필수 dependency가 아니며 별도 asset-tool image/profile과 별도 license BOM으로 격리 |

공통 구성 원칙은 다음과 같습니다.

- tracked text file과 shell script는 LF를 사용하고 canonical command는 POSIX shell
  문법과 repository-relative path로 작성합니다.
- development, production build, E2E, optional asset processing은 서로 다른
  container target 또는 profile로 분리합니다. production artifact에 compiler,
  browser, fixture와 asset tool을 포함하지 않습니다.
- container process는 non-root user로 실행하고 source는 read-only mount를
  우선합니다. dependency cache와 build output만 별도 writable volume을 사용합니다.
- host에서 직접 실행할 때 Vite는 loopback에만 bind합니다. container에서 port를
  publish할 때만 container 전용 설정으로 `0.0.0.0`에 bind하고 공개 network에
  노출하지 않습니다.
- Node.js, npm, Chrome/Chromium과 base image는 floating tag가 아니라 exact
  version으로 고정합니다. Renovate 같은 자동 갱신을 사용하더라도 `npm run verify`
  통과 후에만 lock과 image pin을 갱신합니다.
- source build/test image와 외부 배포 artifact의 license·provenance gate는
  동일하지 않습니다. 아래 `외부 의존성·라이선스 감사`의 `NO-GO` 항목을 container
  전환만으로 해소된 것으로 간주하지 않습니다.

## Linux 컨테이너 전환 검증

2026-07-29 기준 installed dependency 상태에서 typecheck, unit test 6개와 production
build는 Linux/WSL2 x86_64에서 다시 통과했습니다. 기존 검증 기록의 Playwright E2E
16개 통과 결과는 있으나 이번 문서 변경에 대한 E2E 재실행은 요청에 따라 뒤로
미뤘습니다. tracked text file은 LF이며 lockfile에는 Linux glibc와 musl용 native
optional package가 포함되어 있습니다. 따라서 application code를 Windows 전용
구현에서 이식하는 작업은 거의 끝났지만, clean `npm ci`부터 시작하는 재현 가능한
container 개발 환경의 정의와 현재 E2E 재검증은 아직 완료되지 않았습니다.

| 항목 | 현재 상태 | container 전환 판단 |
|---|---|---|
| Container 정의 | Dockerfile, `.dockerignore`, Compose/devcontainer 없음 | **Blocker** — 동일 image를 개발·CI에서 재현할 수 없음 |
| Dev server network | 2D·3D Vite가 `127.0.0.1`에 고정 | **Blocker** — container port publish 후 host에서 접근할 별도 bind 설정 필요 |
| E2E browser | `/usr/bin/google-chrome`을 기본 경로로 가정 | **Blocker** — Chrome/Chromium과 shared library를 image에 설치·고정해야 함 |
| Native ABI | Linux glibc/musl package가 lockfile에 모두 존재 | **Decision** — 우선 glibc를 baseline으로 고정하고 musl은 별도 test matrix로 검증 |
| Clean container gate | 현재 container CI 없음 | **Blocker** — clean `npm ci && npm run verify`를 release gate로 추가해야 함 |
| File·path 규칙 | tracked text는 LF, runtime code는 Node path API 사용 | 통과. 단, 하위 2D README의 Windows drive path는 정리 필요 |
| Offline asset pipeline | ffmpeg·Blender·`toktx`가 core lockfile 밖에 있음 | core 개발은 통과. asset-tool image와 version/license pin은 후속 작업 |

권장 구현 순서는 다음과 같습니다.

1. Node.js 24 기반 glibc image의 digest와 non-root user를 고정한 multi-stage
   Dockerfile 및 `.dockerignore`를 추가합니다.
2. host loopback 기본값은 유지하고 container script에서만 2D `5173`, 3D `3333`
   port를 `0.0.0.0`에 bind합니다.
3. Chrome/Chromium과 필요한 Linux library를 포함한 E2E target을 만들고
   `PLAYWRIGHT_CHROMIUM_EXECUTABLE`을 명시합니다.
4. container 안에서 `npm ci`, `npm run verify`를 수행하는 CI job을 추가하고
   host `node_modules`가 섞이지 않는지 확인합니다.
5. ffmpeg·Blender·`toktx`가 필요한 3D asset pipeline은 별도 image/profile로
   분리하고 core application image에는 포함하지 않습니다.
6. `apps/viewer-2d-sample/README.md`의 standalone Windows layout과 실행 명령을
   현재 npm workspace 및 Linux container 기준으로 갱신합니다.

## 검토 기준

| 항목 | 내용 |
|---|---|
| 기준일 | 2026-07-29 |
| 기준 commit | `cadbd5fb60d300dbb3fdc891fdbbabfbcd5b9109` |
| 기준 tree | 위 commit 이후 이 README 변경 포함 |
| 대상 | `kimminsung/dwg-dxf-viewer-sample` |
| 참조 | 형제 저장소 `hmwebviewer/README.md`의 분석 구조와 3D asset provenance |
| 검토 범위 | workspace 구조, 2D/3D 기능, source·test·build 설정, 입력 경계, npm dependency, WASM·font·decoder·fixture license |
| 기능 판정 | **내부 개발·기능 검증 정확도 및 품질 게이트 무시할 수 있으면 GO** |
| 외부 배포 판정 | **NO-GO — license·source 제공·asset provenance blocker 존재** |

외부 배포 판정은 기술적인 compliance 준비 상태를 뜻합니다. 법률 자문이 아니며,
실제 계약·특허·상표와 배포 승인은 조직의 법무·오픈소스 정책으로 확정해야 합니다.

## 현재 판정

운영 release에는 format별 정확도 contract, 3D 입력 제한·cancellation·Worker 격리,
프로젝트 license, MPL source 제공, third-party 고지와 sample provenance blocker가
남아 있습니다. 특히 “상업 사용 가능한 dependency”와 “현재 artifact를 조건 없이
상업 배포할 수 있음”은 같은 뜻이 아닙니다.


## Workspace 구성

| Workspace | 역할 | 주요 runtime |
|---|---|---|
| `apps/viewer-2d-sample` | DWG/DXF sample UI와 입력 정책 | `@hmwebviewer/viewer2d`, Three.js |
| `packages/viewer2d` | 재사용 가능한 2D parser·renderer module | `dxf-parser`, `opentype.js`, Three.js peer, acadrust WASM |
| `apps/viewer-3d` | 다중 포맷 3D static web application | Three.js, `web-ifc` |

```text
dwg-dxf-viewer-sample/
├─ apps/
│  ├─ viewer-2d-sample/   # 2D sample application
│  └─ viewer-3d/          # 3D sample application과 offline 도구
├─ packages/
│  └─ viewer2d/           # 2D public module
├─ tests/                 # Vitest와 Playwright
├─ LICENSES/              # 현재 MPL-2.0, OFL-1.1 전문
├─ THIRD_PARTY_NOTICES.md
└─ docs/PROVENANCE.md
```

세 workspace는 `"private": true`이며 npm registry 배포용 package가 아닙니다.
`@hmwebviewer/viewer2d`의 export도 현재 TypeScript source를 직접 가리키므로,
독립 배포 package가 아니라 이 workspace 안에서 재사용하는 module입니다.

## 현재 제공 기능

### DWG/DXF 2D viewer

- `.dwg`, `.dxf` URL과 로컬 파일 load
- 파일 선택, Drag & Drop, `?model=<url>` 진입 경로
- DWG는 acadrust WebAssembly parser, DXF는 `dxf-parser`와 adapter 사용
- Three.js orthographic renderer와 pan·zoom
- Zoom Fit, dark/light theme, layer별 표시·숨김
- entity click 선택과 type·layer·handle 표시
- TEXT/MTEXT glyph용 NanumGothic TTF load
- module API의 grid, zoom speed, view-change 구독, entity/block 통계,
  WebP snapshot, 2점 거리 측정, accent color
- page 종료 시 RAF, listener, WebGL resource와 canvas dispose

renderer에는 LINE, CIRCLE, ARC, ELLIPSE, POINT, POLYLINE/LWPOLYLINE,
SPLINE, TEXT/MTEXT/ATTRIB, INSERT, HATCH, DIMENSION, LEADER, XLINE/RAY 등
여러 entity 경로가 있습니다. 모든 DWG/DXF version과 entity 조합의 정확도를
보장한다는 의미는 아닙니다.

2D 입력 경계는 application layer에서 다음과 같이 제한합니다.

- URL은 HTTP(S)와 viewer 동일 origin만 허용
- `Content-Length`와 streaming 누적 크기를 모두 확인
- 로컬·URL 입력 최대 50 MiB
- URL fetch timeout 15초

### 다중 포맷 3D viewer

- `?model=<url>` server asset load와 로컬 Drag & Drop
- GLB, GLTF, OBJ, FBX, DAE, IFC, PLY load
- OBJ와 함께 드롭한 MTL 및 PNG/JPEG/BMP/GIF/WebP/TGA texture 연결
- 300 MiB 초과 OBJ의 2-pass streaming parser
- 큰 절대 좌표를 가진 OBJ의 float64 source 단계 재중심화
- Draco geometry와 KTX2/Basis texture runtime decode
- IFC single-thread WebAssembly parse와 geometry 변환
- PLY mesh·point cloud 처리 및 Float64 attribute의 Float32 정규화
- OrbitControls, Zoom Fit, 원근·직교 projection, outline 표시
- FPS overlay와 지속적인 저 FPS에서 pixel ratio를 낮추는 adaptive quality
- model URL과 이름이 같은 360° WebP preview를 먼저 표시한 뒤 canvas로 전환
- Vite `BASE_URL`을 따르는 decoder·preview 경로와 subpath 배포
- 교체 model의 geometry·material·texture와 Blob URL 정리

WebP preview는 미리 생성된 정적 파일입니다. Vite가 HTML shell을 제공한 뒤
브라우저에서 preview와 model을 load하므로 server-side 3D rendering 또는 SSR
구현은 없습니다.

## 실행 구조

```mermaid
flowchart TD
    A["2D File / same-origin URL"] --> B["cadInputPolicy"]
    B --> C["parseCad"]
    C --> D["acadrust WASM<br/>DWG"]
    C --> E["dxf-parser + adapter<br/>DXF"]
    D --> F["CadParseResult"]
    E --> F
    F --> G["Viewer2D / Three.js"]

    H["3D File / ?model URL"] --> I["ThreeDViewer"]
    I --> J["modelLoader"]
    J --> K["Three.js format loaders"]
    J --> L["web-ifc JS + WASM"]
    K --> M["THREE.Object3D"]
    L --> M
    M --> N["WebGLRenderer"]
```

2D application은 `packages/viewer2d` 내부 파일을 직접 import하지 않고
`@hmwebviewer/viewer2d` public interface를 사용합니다. 3D `modelLoader`는
확장자로 loader를 선택하고 결과를 `THREE.Object3D`로 정규화합니다.

`npm ci`의 root `postinstall`은 Three.js의 Draco·Basis 파일과 `web-ifc` WASM을
`apps/viewer-3d/public` 아래에 staging합니다. runtime에서는 설치된 Three.js와
동일한 decoder를 사용합니다. `web-ifc-mt.wasm`도 복사되지만 현재 code path는
single-thread `web-ifc.wasm`만 사용합니다.

## 지원 범위와 알려진 제한

### 2D

| 포맷 | URL | 로컬 파일 | 현재 검증과 제한 |
|---|---:|---:|---|
| DWG | 동일 origin 지원 | 지원 | `BasicSample.dwg` 1개 fixture 중심. acadrust WASM wrapper의 재현 build source가 없음 |
| DXF | 동일 origin 지원 | 지원 | `simple.dxf`와 public parser contract 검증. binary DXF 및 전체 entity fidelity 미검증 |

### 3D

| 포맷 | `?model` URL | 로컬 파일 | 알려진 제한 |
|---|---:|---:|---|
| GLB | 지원 | 지원 | Draco/KTX2 지원. 손실 압축 허용오차와 원본 대비 fidelity 기준 없음 |
| GLTF | 지원 | 제한적 | 로컬 `.bin`·texture sidecar를 함께 해석하는 file-set session 없음 |
| OBJ | 지원 | 지원 | URL 경로의 MTL 자동 load 없음. 로컬만 MTL·texture sidecar 지원 |
| OBJ > 300 MiB | 제한적 | 지원 | URL에는 size 기반 streaming 분기가 없음. 로컬 streaming path는 smooth normal과 MTL `Kd` color만 사용하고 texture·hard edge fidelity를 포기 |
| FBX | 지원 | 지원 | 축·단위·animation·외부 texture 보존 계약 없음 |
| DAE | 지원 | 제한적 | 로컬 외부 texture sidecar 미지원 |
| IFC | 지원 | 지원 | geometry와 color만 Three.js object로 변환. property/semantic API와 원점 이동 기록 미보존 |
| PLY | 지원 | 지원 | mesh 또는 point cloud. double attribute는 GPU용 Float32로 변환되어 정밀도 손실 가능 |

여기서 “지원”은 sample을 파싱해 renderable object를 만들 수 있다는 뜻입니다.
CAD/BIM 원본과의 좌표·단위·원점·topology·재질·속성 일치를 의미하지 않습니다.

## 신뢰 경계와 운영 위험

2D 경로는 same-origin, 50 MiB, 15초 제한을 적용하지만 magic bytes, MIME,
압축 해제 후 entity 수와 geometry 복잡도 quota는 없습니다.

3D `?model`은 URL scheme·origin·크기·timeout을 제한하지 않습니다. browser CORS가
허용하면 외부 origin도 요청할 수 있고, GLTF/DAE/FBX/OBJ가 참조하는 URI가 추가
network request를 만들 수 있습니다. 로컬 3D 파일에도 최대 크기와 decode 후
vertex·texture·memory quota가 없습니다.

두 viewer 모두 parser와 상당한 geometry 처리를 browser main thread에서 수행합니다.
운영 배포에는 format별 Worker 격리, URL allowlist, CSP, MIME·signature 검사,
streaming byte limit, decode complexity quota, timeout과 cancellation이 필요합니다.

동시에 여러 load를 시작했을 때 이전 요청을 취소하거나 latest-wins를 보장하는
session 규약도 없습니다. 느린 이전 요청이 나중 요청 뒤에 완료되어 화면을
덮을 수 있습니다.

현재 E2E는 sample render, finite vertex, canvas lifecycle과 subpath decoder URL을
검사합니다. 공식 validator, golden geometry, 좌표·단위 왕복, topology,
material·IFC property contract와 장시간 memory soak는 release gate에 없습니다.

## 외부 의존성·라이선스 감사

> **배포 준비 판정: 외부 demo·고객 전달·public CDN·commercial SaaS `NO-GO`,
> 조직 내부 개발·검증 조건부 `GO`**
>
> dependency 자체는 대부분 상업 사용 가능한 license이지만, 프로젝트 소유 코드의
> license, MPL source 제공, Apache/MIT 고지와 sample provenance가 완결되지 않았습니다.

감사 범위는 `package-lock.json`의 `node_modules/*` entry, 실제 browser bundle,
`public` 정적 파일, bundled WASM·font, sample과 파생 preview, repository가
호출하거나 언급하는 system tool입니다.

lockfile에는 142개 `node_modules/*` entry가 있습니다. 이 중 3개는 이 저장소의
workspace link이고 제3자 package는 139개입니다. 제3자 entry의 license metadata
누락은 0개입니다. runtime entry는 5개, build/dev entry는 134개이며 그중 57개는
platform별 optional package입니다.

### Browser runtime과 직접 배포되는 구성요소

| 구성요소 | 버전·출처 | 역할·배포 위치 | License | 현재 compliance 상태 |
|---|---|---|---|---|
| Three.js | `0.185.1` | 두 viewer의 JS bundle, loader·controls | MIT | package license는 확인. 배포 artifact에 통합 MIT notice 없음 |
| dxf-parser | `1.1.2` | DXF parser JS bundle | MIT | package license는 확인. 통합 notice 없음 |
| loglevel | `1.9.2`, `dxf-parser` transitive | DXF runtime logging | MIT | lockfile·package license 확인 |
| opentype.js | `2.0.0` | TTF glyph 처리 JS bundle | MIT | package license는 확인. 통합 notice 없음 |
| acadrust | WASM 내부 `0.4.1` | `acadrust_dwg_bg.wasm`, DWG parse | MPL-2.0 | MPL 전문과 checksum은 있음. wrapper source·toolchain과 재현 build가 없음 |
| NanumGothic | Google Fonts 원본 checksum 일치 | `NanumGothic-Regular.ttf` | OFL-1.1 | 전문과 checksum은 있음. root license 파일이 app dist에는 자동 포함되지 않음 |
| web-ifc | `0.0.77` | dynamic JS bundle, `/web-ifc/*.wasm` | MPL-2.0 | package 전문은 확인. exact covered source 취득 안내가 배포물에 없음 |
| Draco decoder | Three.js r185 배포본 | `/draco/*.js`, `/draco/*.wasm` | Apache-2.0 | source와 byte-for-byte staging test 있음. Apache 전문·NOTICE가 app dist에 없음 |
| Basis Universal transcoder | Three.js r185 배포본 | `/basis/*.js`, `/basis/*.wasm` | Apache-2.0 | `basis/README.md`에 upstream link만 있음. Apache 전문·NOTICE가 app dist에 없음 |

저장소의 [`THIRD_PARTY_NOTICES.md`](./THIRD_PARTY_NOTICES.md)는 acadrust,
NanumGothic과 주요 runtime library를 기록하고, [`LICENSES/`](./LICENSES/)에는
MPL-2.0과 OFL-1.1 전문이 있습니다. 그러나 이 파일들은 두 Vite application의
`public` 아래에 있지 않아 현재 `npm run build` 결과에는 포함되지 않습니다.

### 전체 npm license 집계

| License | 제3자 package entry 수 | 주 사용 범위 | 상업 사용 | source 공개 영향 |
|---|---:|---|---:|---|
| MIT | 86 | runtime 4개와 build/dev | 가능 | 프로젝트 source 공개 의무 없음. copyright·license notice 보존 |
| Apache-2.0 | 31 | TypeScript, Playwright, Puppeteer와 platform package | 가능 | 프로젝트 source 공개 의무 없음. license·변경·NOTICE·patent 조건 적용 |
| MPL-2.0 | 13 | `web-ifc` runtime 1개, Lightning CSS 계열 dev 12개 | 가능 | 외부 executable 배포 시 covered source와 취득 안내 필요 |
| ISC | 6 | build/dev | 가능 | copyright·permission notice 보존 |
| BSD-3-Clause | 2 | build/dev | 가능 | notice·면책 보존, 이름을 홍보에 사용하지 않음 |
| 0BSD | 1 | optional build/dev | 가능 | source 공개와 attribution 의무 없음 |

위 집계에는 npm 밖의 acadrust WASM, NanumGothic, Draco, Basis와 sample asset
license가 포함되지 않습니다. `UNKNOWN` 3개처럼 보이는 lockfile entry는
`@hmwebviewer/*` first-party workspace link이며 제3자 license 누락이 아닙니다.

#### MPL-2.0 npm package 전체 목록

`package-lock.json`에서 `license: "MPL-2.0"`으로 선언된 13개 entry를 전부
나열하면 다음과 같습니다. 이는 **13개의 독립 codebase**를 의미하지 않습니다.
`web-ifc` 1개와, 동일한 Lightning CSS source에서 배포되는 core package 1개 및
platform별 optional native package 11개로 구성됩니다.

| npm package | Version | 설치·실행 범위 | 공식 source repository |
|---|---:|---|---|
| `web-ifc` | `0.0.77` | production runtime. IFC parser JS가 bundle되고 single-thread WASM이 application public asset으로 staging됨 | [ThatOpen/engine_web-ifc](https://github.com/ThatOpen/engine_web-ifc) |
| `lightningcss` | `1.33.0` | Vite build dependency. CSS parse·transform·minify core와 현재 platform용 native binding 선택 | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-android-arm64` | `1.33.0` | optional dev package, Android ARM64 native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-darwin-arm64` | `1.33.0` | optional dev package, macOS ARM64 native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-darwin-x64` | `1.33.0` | optional dev package, macOS x86_64 native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-freebsd-x64` | `1.33.0` | optional dev package, FreeBSD x86_64 native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-linux-arm-gnueabihf` | `1.33.0` | optional dev package, Linux ARM hard-float GNU native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-linux-arm64-gnu` | `1.33.0` | optional dev package, Linux ARM64 glibc native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-linux-arm64-musl` | `1.33.0` | optional dev package, Linux ARM64 musl native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-linux-x64-gnu` | `1.33.0` | optional dev package, Linux x86_64 glibc native binding. 현재 표준 container 대상 | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-linux-x64-musl` | `1.33.0` | optional dev package, Linux x86_64 musl native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-win32-arm64-msvc` | `1.33.0` | optional dev package, Windows ARM64 MSVC native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |
| `lightningcss-win32-x64-msvc` | `1.33.0` | optional dev package, Windows x86_64 MSVC native binding | [parcel-bundler/lightningcss v1.33.0](https://github.com/parcel-bundler/lightningcss/tree/v1.33.0) |

Lightning CSS의 12개 entry는 모두 동일한 MPL-2.0 source tree와 release version을
가리키지만 lockfile 및 SBOM에서는 서로 다른 배포 package로 유지합니다. 실제 install
시 npm은 host OS·CPU·libc와 맞는 optional binding만 선택합니다. 이 project의
production browser artifact에는 Lightning CSS code가 포함되지 않지만, build
container 또는 `node_modules`를 제3자에게 전달한다면 선택된 binding과 covered
source 제공 방법을 함께 관리해야 합니다.

`web-ifc@0.0.77` package가 선언한 공식 repository는 위 root URL이며 package
metadata에는 exact source commit(`gitHead`)이 없습니다. 따라서 외부 배포 전에
registry tarball과 대응하는 source commit/archive를 별도로 고정하고, 수정 여부와
covered source 취득 방법을 배포 고지에 기록해야 합니다.

### Build·test·offline 도구

| 구성요소 | lockfile 기준 | 역할 | License·배포 판단 |
|---|---:|---|---|
| Vite | `8.1.5` | dev server와 production build | MIT. build output에는 runtime third-party notice를 별도로 넣어야 함 |
| TypeScript | `7.0.2` | typecheck | Apache-2.0. compiler를 고객에게 함께 전달하지 않으면 제품 source 공개 영향 없음 |
| Vitest | `4.1.10` | unit test | MIT, 제품 runtime 아님 |
| Playwright | `1.62.0` | browser E2E | Apache-2.0, 제품 runtime 아님 |
| Puppeteer Core | `25.4.0` resolved | 3D smoke·preview capture | Apache-2.0. Chrome binary를 포함하지 않음 |
| Lightning CSS | `1.33.0` 계열 12 entry | Vite build transitive | MPL-2.0. build host에서만 사용하며 browser runtime에는 포함되지 않음 |
| Node.js·npm | Node >=24, npm >=11 요구 | install, build, tool runtime | 함께 재배포하면 각 runtime과 bundled dependency license를 별도 감사 |
| Chrome | system executable | Playwright/Puppeteer 실행 | repository가 bundle하지 않음. CI image·설치형 배포에 포함하면 vendor 조건 별도 검토 |
| ffmpeg | optional, lockfile 밖 | PNG frame을 animated WebP로 결합 | binary를 bundle할 때 실제 build option의 LGPL/GPL/nonfree 조건 확인 |
| KTX-Software `toktx` | 도입 안내만 존재 | 선택적 KTX2 encoding | 현재 dependency가 아님. 도입 시 exact binary BOM과 license 재검토 |

`apps/viewer-3d/tools/preprocess.mjs`는 `@gltf-transform/core`,
`@gltf-transform/functions`, `@gltf-transform/extensions`, `draco3d`를 import하지만
현재 `package.json`과 lockfile에는 이 package들이 없습니다. 따라서 clean install의
정식 toolchain으로 재현되지 않습니다.

현재 script가 구현한 texture output은 WebP/JPEG/PNG이며 KTX2 encoding은 하지
않습니다. KTX2는 별도 glTF Transform CLI와 `toktx`가 필요합니다.
하위 `tools/README.md`의 “KTX2 default” 설명은 현재 code와 일치하지 않습니다.

### MPL·Apache·OFL 적용 범위

MPL-2.0은 file-level copyleft입니다. 별도 파일로 결합한 이 application 전체를
MPL로 공개할 필요는 없지만, 조직 밖으로 browser JS/WASM executable을 전달하면
수령자가 해당 MPL covered source와 수정분을 합리적인 방법으로 얻을 수 있게
알려야 합니다. browser로 전송되는 client code도 배포에 해당합니다.

- `web-ifc@0.0.77`: exact source archive 또는 고정 commit/tag URL과 수정 여부,
  build 가능한 covered source 취득 안내가 필요
- acadrust DWG WASM: crate source만으로는 현재 binary wrapper를 재현할 수 없으므로
  wrapper source·Cargo/wasm-bindgen 설정·toolchain을 복구하기 전 외부 배포 보류
- Lightning CSS: `node_modules`나 build container를 외부에 전달하지 않고 build
  host에서만 실행하면 application source 공개 범위를 만들지 않음

Apache-2.0의 Draco와 Basis는 source 공개 의무는 없지만 license 사본, 기존
attribution과 upstream NOTICE, 수정 시 변경 고지를 보존해야 합니다. OFL font는
software와 bundle할 수 있지만 copyright와 OFL 전문을 함께 제공해야 하며,
Reserved Font Name 조건을 지켜야 합니다.

참조:

- [Mozilla MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/)
- [acadrust upstream](https://github.com/hakanaktt/acadrust)
- [web-ifc upstream](https://github.com/ThatOpen/engine_web-ifc)
- [Three.js r185 MIT license](https://github.com/mrdoob/three.js/blob/r185/LICENSE)
- [Google Draco Apache-2.0 license](https://github.com/google/draco/blob/main/LICENSE)
- [Basis Universal](https://github.com/BinomialLLC/basis_universal)
- [NanumGothic OFL](https://github.com/google/fonts/blob/main/ofl/nanumgothic/OFL.txt)

### Sample asset과 파생 preview

Sample과 preview는 software dependency license와 별개의 저작물입니다.
optimized model과 회전 preview도 원본 asset의 license·attribution·변경 표시를
계승해야 합니다.

| 자산 | 확인된 출처·license | 현재 판단 |
|---|---|---|
| `BasicSample.dwg` | 2D standalone initial commit에서 이관, 원출처 불명 | 내부 회귀 test 전용. 외부 demo 배포 보류 |
| `simple.dxf` | 2D standalone initial commit의 최소 fixture | 권리자·license 명시가 없어 외부 재배포 승인 필요 |
| `Box.glb` | Khronos sample, Cesium 제공, CC BY 4.0 | 상업 사용 가능하나 Cesium attribution, license link와 변경 여부 고지가 현재 배포물에 없음 |
| `Duck.glb` | Copyright 2006 Sony Computer Entertainment Inc., SCEA Shared Source License 1.0 | 비표준 license. 내부 법무 승인 전 외부 배포 보류 |
| `Duck.optimized.glb`, `Duck.webp` | Duck의 Draco/WebP·preview 파생물 | 원본 SCEA 승인과 derivative 변경 표시에 종속 |
| `Cube.obj`, `Cube.dae`, `Cube.ifc`, `DoublePrecision.ply` | 파일·commit에 hand-authored/project fixture 정황 | 프로젝트 권리자와 first-party license가 선언되지 않아 외부 배포 보류 |
| `Cube.fbx`, `sample.mtl` | history에는 Three.js `morph_test`와 Blender 정황만 있고 exact 원본 URL·asset license 없음 | provenance 복구 또는 제거 전 외부 배포 보류 |
| `Cube.*.webp` | 각 Cube model에서 생성한 24-frame preview | 원본 model의 권리·license와 변경 표시에 종속 |

Box와 Duck의 upstream license:

- [Khronos Box — CC BY 4.0](https://github.com/KhronosGroup/glTF-Sample-Models/blob/main/2.0/Box/README.md)
- [Khronos Duck — SCEA Shared Source License 1.0](https://github.com/KhronosGroup/glTF-Sample-Models/blob/main/2.0/Duck/README.md)

fixture checksum과 source history는 [`docs/PROVENANCE.md`](./docs/PROVENANCE.md),
acadrust·font checksum은 [`THIRD_PARTY_NOTICES.md`](./THIRD_PARTY_NOTICES.md)를
기준으로 관리합니다. 현재 provenance 문서는 2D fixture 중심이며 3D asset과
preview derivative chain은 아직 통합되지 않았습니다.

### 프로젝트 license와 외부 release gate

루트에는 프로젝트 소유 코드에 적용되는 `LICENSE`와 copyright holder 선언이
없습니다. `package.json`의 `"private": true`는 npm publish 방지 설정일 뿐
외부 이용·수정·재배포 권한을 부여하지 않습니다. 하위 과거 README의
“Viewer2D 포트·샘플 글루 MIT” 문구도 현재 repository의 license grant로 볼 수
없습니다.

다음 조건을 완료하기 전에는 외부 demo, 고객 전달, 설치형 package, public CDN과
commercial SaaS 배포를 승인하지 않습니다.

- 프로젝트 소유 코드의 권리자와 proprietary/open-source license 결정
- 2D·3D build artifact에 runtime `THIRD_PARTY_NOTICES`와 필요한 license 전문 포함
- acadrust wrapper source·toolchain 복구와 exact WASM 재현 build
- acadrust 및 `web-ifc@0.0.77` covered source의 고정 archive/URL과 취득 안내 제공
- Draco·Basis Apache license와 upstream NOTICE 보존
- Box attribution과 sample별 원본 URL·권리자·license·checksum·derivative chain 기록
- Duck의 내부 법무 승인 또는 승인된 CC0/CC BY asset으로 교체
- `BasicSample.dwg`, `Cube.fbx`, `sample.mtl` 등 출처 미완료 fixture의 승인 또는 제거
- 미사용 `web-ifc-mt.wasm`과 중복 decoder artifact의 배포 필요성 결정
- runtime/build/sample을 구분한 SBOM과 CI의 unknown-license/provenance gate

## 설치와 실행

요구 환경은 Node.js 24 이상, npm 11 이상과 WebGL을 지원하는 browser입니다.

```bash
npm ci
```

`npm ci`는 workspace dependency를 설치한 뒤 3D decoder와 WASM을 staging합니다.

```bash
npm run dev:2d
# http://127.0.0.1:5173

npm run dev:3d
# http://127.0.0.1:3333
```

URL load 예시:

```text
http://127.0.0.1:5173/?model=/samples/simple.dxf
http://127.0.0.1:3333/?model=/samples/Box.glb
```

Production build와 preview:

```bash
npm run build
npm --workspace @hmwebviewer/viewer-2d-sample run preview
npm --workspace @hmwebviewer/viewer-3d run preview
```

## 검증

```bash
npm run typecheck
npm run test:unit
npm run build
npm run test:e2e
```

전체 순차 검증:

```bash
npm run verify
```

2026-07-29 실제 검증 결과:

- workspace typecheck 통과
- Vitest 5개 file, 6개 test 통과
- 2D·3D production build 통과
- Playwright 16개 E2E 통과, 29.3초
- build 실패는 없지만 2D main bundle, Three.js, web-ifc와 decoder JS에
  500 kB 초과 chunk 경고가 있음

현재 test가 확인하는 범위:

- 2D public package interface의 DXF parse
- 2D same-origin·50 MiB 입력 정책
- canonical module map 문서 drift
- decoder WASM의 installed dependency 대비 checksum과 실행 권한
- Vite subpath의 public asset URL
- 2D DWG/DXF sample과 local file load
- 3D GLB, Draco GLB, OBJ, FBX, DAE, IFC, PLY sample render
- 2D/3D canvas dispose와 반복 page transition
- production subpath의 Draco·IFC decoder request
- 2D DWG/DXF와 3D GLB/PLY evidence screenshot

이 검증은 sample이 화면에 나타나고 runtime error가 없다는 smoke/contract 수준입니다.
engineering 정확도와 외부 배포 compliance를 승인하는 test는 아닙니다.

## 문서

- [Third-party notices](./THIRD_PARTY_NOTICES.md)
- [Source와 fixture provenance](./docs/PROVENANCE.md)
- [DWG/DXF module map](./docs/modules-dwg-dxf.html)
- [2D sample 안내](./apps/viewer-2d-sample/README.md)
- [3D architecture](./apps/viewer-3d/docs/architecture.html)
- [3D 구현 계획](./apps/viewer-3d/PLAN.md)
- [3D 검증 기록](./apps/viewer-3d/PROGRESS.md)
- [3D asset 도구](./apps/viewer-3d/tools/README.md)
- [초기 3D 설계서](./apps/viewer-3d/3d_viewer_architecture_spec.pdf)

하위 2D README에는 통합 전 standalone layout이, 3D tools README에는 과거 KTX2
계획이 남아 있습니다. 현재 workspace의 canonical 개요와 배포 판단은 이 문서를
기준으로 하며, 하위 문서는 후속 정합성 정리가 필요합니다.
