# 機能設計書 22-bun publish

## 概要

本ドキュメントは、Bunパッケージマネージャーの `bun publish` コマンドの機能設計を記述する。このコマンドは、npmパッケージをnpmレジストリに公開するために使用される。

### 本機能の処理概要

`bun publish` は、ローカルで開発したnpmパッケージをtarball形式にパッキングし、npmレジストリ（デフォルトはnpmjs.org）にアップロードして公開する機能を提供する。

**業務上の目的・背景**：オープンソースまたは社内パッケージの配布において、npmレジストリへの公開は標準的なワークフローである。Bunは高速なパッケージマネージャーとして、この公開プロセスも効率的に行える機能を提供する。npm互換のpublish機能により、既存のnpmワークフローからスムーズに移行できる。

**機能の利用シーン**：
- 新しいnpmパッケージの初回公開
- 既存パッケージの新バージョンリリース
- プライベートレジストリへのパッケージ公開
- CI/CDパイプラインでの自動リリース

**主要な処理内容**：
1. package.jsonの解析とバリデーション（name、version、private等）
2. パッケージのtarball作成（bun packと同等の処理）
3. SHA1/SHA512ハッシュの計算（整合性検証用）
4. レジストリ認証情報の取得（.npmrc、環境変数等）
5. OTP（ワンタイムパスワード）認証の処理（必要に応じて）
6. HTTPリクエストでレジストリにアップロード
7. publish/postpublishスクリプトの実行

**関連システム・外部連携**：
- npmレジストリ（registry.npmjs.org等）
- プライベートnpmレジストリ（Verdaccio等）
- Web認証（authUrl経由のブラウザ認証）

**権限による制御**：レジストリへの認証が必要。スコープ付きパッケージの場合、適切なpublish権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 14 | publish | 主画面 | パッケージのnpmレジストリへの公開処理 |

## 機能種別

データ連携（外部API通信）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| tarball_path | string | No | 公開するtarballのパス（省略時はカレントディレクトリをpack） | ファイル存在確認 |
| --tag | string | No | 公開時のdist-tag（デフォルト: latest） | - |
| --access | string | No | パッケージのアクセスレベル（public/restricted） | スコープ付きパッケージでのみrestricted可 |
| --dry-run | boolean | No | 実際にpublishせずにシミュレーション | - |
| --otp | string | No | 二要素認証のワンタイムパスワード | - |
| --auth-type | string | No | 認証タイプ（legacy/web） | - |
| --tolerate-republish | boolean | No | 既存バージョン再公開時にエラーとしない | - |

### 入力データソース

- カレントディレクトリのpackage.jsonファイル
- tarballファイル（パス指定時）
- .npmrc（認証情報）
- bunfig.toml（レジストリ設定）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| packed files | string[] | パッケージに含まれるファイル一覧 |
| package size | number | 圧縮後のサイズ |
| unpacked size | number | 展開後のサイズ |
| shasum | string | SHA1ハッシュ（40文字hex） |
| integrity | string | SHA512ハッシュ（Base64） |
| tag | string | 適用されたdist-tag |
| access | string | アクセスレベル |
| registry | string | 公開先レジストリURL |
| success message | string | `+ {name}@{version}` |

### 出力先

- 標準出力（コンソール）
- npmレジストリ（HTTPリクエスト）

## 処理フロー

### 処理シーケンス

```
1. コマンドライン引数の解析
   └─ --tag, --access, --dry-run, --otp等のオプション解析
2. パッケージマネージャーの初期化
   └─ レジストリ設定、認証情報の読み込み
3. tarball準備
   ├─ パス指定時: tarballファイルを読み込み
   └─ パス未指定時: カレントディレクトリをpack
4. package.jsonの解析と検証
   ├─ name, version必須チェック
   ├─ private: trueの場合はエラー
   └─ publishConfig設定の適用
5. ハッシュ計算
   ├─ SHA1 (shasum)
   └─ SHA512 (integrity)
6. パッケージ情報の正規化
   └─ bin, dist, _id等の設定
7. 認証確認
   ├─ token認証
   └─ Basic認証
8. 既存バージョンチェック（--tolerate-republish時）
   └─ レジストリにバージョンが存在するか確認
9. publishリクエスト送信
   └─ PUT /{package-name} with JSON body
10. OTP認証処理（必要時）
    ├─ Web認証（authUrl）
    └─ コンソール入力
11. レスポンス処理
    └─ エラー時はメッセージ表示
12. スクリプト実行
    ├─ publish
    └─ postpublish
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[引数解析]
    B --> C[PackageManager初期化]
    C --> D{tarballパス指定?}
    D -->|Yes| E[tarball読み込み]
    D -->|No| F[カレントディレクトリpack]
    E --> G[package.json解析]
    F --> G
    G --> H{private: true?}
    H -->|Yes| I[エラー: private package]
    H -->|No| J{name/version存在?}
    J -->|No| K[エラー: missing field]
    J -->|Yes| L[ハッシュ計算]
    L --> M{認証情報あり?}
    M -->|No| N[エラー: need auth]
    M -->|Yes| O{--dry-run?}
    O -->|Yes| P[サマリー出力]
    O -->|No| Q{--tolerate-republish?}
    Q -->|Yes| R{バージョン存在?}
    R -->|Yes| S[スキップ]
    R -->|No| T[publishリクエスト]
    Q -->|No| T
    T --> U{401 OTP必要?}
    U -->|Yes| V[OTP取得]
    V --> W[OTP付きリクエスト]
    U -->|No| X{成功?}
    W --> X
    X -->|No| Y[エラー出力]
    X -->|Yes| Z[成功メッセージ]
    Z --> AA[スクリプト実行]
    AA --> AB[終了]
    P --> AB
    S --> AB
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-22-01 | name必須 | package.jsonにname属性が必要 | 常時 |
| BR-22-02 | version必須 | package.jsonにversion属性が必要 | 常時 |
| BR-22-03 | private禁止 | private: trueのパッケージは公開不可 | 常時 |
| BR-22-04 | restricted制限 | restrictedアクセスはスコープ付きパッケージのみ | access=restricted時 |
| BR-22-05 | 認証必須 | レジストリへの認証情報が必要 | 常時 |
| BR-22-06 | OTP処理 | 二要素認証が有効な場合はOTPが必要 | レジストリ要求時 |

### 計算ロジック

**SHA1ハッシュ計算**:
```
shasum = SHA1(tarball_bytes).hex()
```

**SHA512整合性計算**:
```
integrity = "sha512-" + Base64(SHA512(tarball_bytes))
```

## データベース操作仕様

### 操作別データベース影響一覧

本機能はデータベースを使用しない。外部APIとの通信のみ。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | MissingPackageName | package.jsonにname属性がない | name属性を追加 |
| - | MissingPackageVersion | package.jsonにversion属性がない | version属性を追加 |
| - | PrivatePackage | private: trueが設定されている | privateを削除またはfalseに |
| - | RestrictedUnscopedPackage | スコープなしでrestricted指定 | publicに変更またはスコープを追加 |
| - | NeedAuth | 認証情報がない | bunx npm loginを実行 |
| 401 | UnauthorizedOTP | OTPが必要 | OTPを入力またはWeb認証 |
| 403 | Forbidden | 権限不足 | パッケージの権限を確認 |
| 409 | Conflict | バージョンが既に存在 | バージョンを更新 |

### リトライ仕様

OTP認証失敗時はユーザー入力を待って再試行。その他のエラーはリトライなし。

## トランザクション仕様

トランザクション管理なし。publish処理は1回のHTTPリクエストで完了する。失敗した場合、レジストリ側でロールバックされる。

## パフォーマンス要件

- tarball作成: ファイル数とサイズに依存
- アップロード: ネットワーク速度とtarballサイズに依存
- 一般的なパッケージで数秒から数十秒

## セキュリティ考慮事項

- 認証トークンは環境変数または.npmrcで管理
- OTP認証によるアカウント保護
- HTTPSによる通信暗号化
- 認証情報のログ出力を回避（redacted）

## 備考

- npm互換のpublish処理を実装
- publishConfigでtag、access、registryをpackage.jsonで設定可能
- Verdaccioなどのプライベートレジストリに対応

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | publish_command.zig | `src/cli/publish_command.zig` | Context構造体（34行目〜）でpublish時に必要な情報を管理 |

**読解のコツ**: Zigのジェネリック型 `Context(comptime directory_publish: bool)` に注目。tarball指定とworkspace指定で処理を分岐。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | publish_command.zig | `src/cli/publish_command.zig` | exec関数（290行目）がエントリーポイント |

**主要処理フロー**:
1. **291行目**: バージョン表示
2. **294行目**: コマンドライン引数解析
3. **296-304行目**: PackageManager初期化
4. **307-354行目**: tarballパス指定時の処理（fromTarballPath）
5. **356-379行目**: ワークスペースからのpack処理（fromWorkspace）
6. **384-398行目**: publish関数呼び出しと成功メッセージ
7. **400-447行目**: publish/postpublishスクリプト実行

#### Step 3: tarball処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | publish_command.zig | `src/cli/publish_command.zig` | fromTarballPath関数（34-234行目） |

**主要処理フロー**:
- **47-50行目**: tarball読み込み
- **54-64行目**: Archive.Iteratorでtarball解析
- **105-113行目**: package.json抽出
- **142-148行目**: private: trueチェック
- **150-167行目**: publishConfig解析
- **184-196行目**: SHA1/SHA512計算

#### Step 4: publish処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | publish_command.zig | `src/cli/publish_command.zig` | publish関数（531-730行目） |

**主要処理フロー**:
- **535-539行目**: 認証確認
- **541-555行目**: tolerate-republish処理
- **557-570行目**: サマリー出力
- **572-608行目**: HTTPリクエスト構築と送信
- **620-730行目**: レスポンス処理とOTP対応

#### Step 5: OTP認証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | publish_command.zig | `src/cli/publish_command.zig` | getOTP関数（752-920行目） |

**主要処理フロー**:
- **759-769行目**: レスポンスJSONからauthUrl取得
- **770-807行目**: ブラウザ認証UI表示
- **825-907行目**: doneUrlポーリング
- **910-919行目**: コンソール入力フォールバック

### プログラム呼び出し階層図

```
PublishCommand.exec
    │
    ├─ PackageManager.init
    │
    ├─ Context.fromTarballPath (tarball指定時)
    │      ├─ File.readFrom
    │      ├─ Archive.Iterator
    │      ├─ JSON.parsePackageJSONUTF8
    │      ├─ sha.SHA1.init/update/final
    │      └─ sha.SHA512.init/update/final
    │
    ├─ Context.fromWorkspace (workspace時)
    │      ├─ Lockfile.loadFromCwd
    │      └─ Pack.pack
    │
    ├─ publish
    │      ├─ checkPackageVersionExists (tolerate-republish時)
    │      ├─ constructPublishRequestBody
    │      ├─ constructPublishHeaders
    │      ├─ http.AsyncHTTP.initSync/sendSync
    │      └─ getOTP (OTP必要時)
    │
    └─ Run.runPackageScriptForeground (スクリプト実行)
```

### データフロー図

```
[入力]               [処理]                    [出力]

package.json ───▶ バリデーション ───▶ エラー/続行
     │
     ▼
tarball作成 ────▶ ハッシュ計算 ────▶ shasum, integrity
     │
     ▼
認証情報 ───────▶ HTTPリクエスト ───▶ npmレジストリ
                      │
                      ▼
              401レスポンス時
                      │
                      ▼
OTP入力 ────────▶ 再リクエスト ────▶ npmレジストリ
                      │
                      ▼
                 成功メッセージ ───▶ 標準出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| publish_command.zig | `src/cli/publish_command.zig` | ソース | publishコマンドのメイン実装 |
| pack_command.zig | `src/cli/pack_command.zig` | ソース | tarball作成処理 |
| PackageManager.zig | `src/install/PackageManager.zig` | ソース | パッケージマネージャー初期化 |
| npm.zig | `src/install/npm.zig` | ソース | npmレジストリ通信 |
| http.zig | `src/http.zig` | ソース | HTTP通信処理 |
| sha.zig | `src/sha.zig` | ソース | ハッシュ計算 |
