Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add file I/O utilities for snapshot serialization (depends on promidas v1.1.0) #8

@F88

Description

@F88

概要

promidas v1.1.0 で追加される新機能(getSerializableSnapshot(), setupSnapshotFromSerializedData())を活用し、Node.js環境でのファイルI/O機能を promidas-utils に追加する。

背景

promidas v1.1.0 の新機能

promidas に以下のメソッドが追加される予定(PR #81):

// JSONオブジェクトとしてスナップショットを取得
getSerializableSnapshot(): SnapshotSuccess | SnapshotFailure

// JSONオブジェクトからスナップショットを復元
setupSnapshotFromSerializedData(data: unknown): SnapshotSuccess | SnapshotFailure

これらはプラットフォーム非依存で、ブラウザ・Node.js・Deno・Bunすべてで動作する。

promidas-utils の役割

Node.js環境でのファイルI/Oを担当し、開発者がより簡単にスナップショットの永続化を行えるようにする。

提案する機能

API設計

// @f88/promidas-utils/repository

/**
 * Export snapshot to JSON file
 */
export async function exportSnapshotToFile(
  repository: ProtopediaInMemoryRepository,
  filePath: string,
  options?: {
    pretty?: boolean      // JSON formatting (default: false)
    logger?: Logger       // Optional logger
  }
): Promise<FileExportResult>

/**
 * Import snapshot from JSON file
 */
export async function importSnapshotFromFile(
  repository: ProtopediaInMemoryRepository,
  filePath: string,
  options?: {
    logger?: Logger       // Optional logger
  }
): Promise<FileImportResult>

Result Types

export type FileExportSuccess = {
  ok: true
  filePath: string
  bytesWritten: number
  prototypesExported: number
  snapshot: SerializableSnapshot
}

export type FileExportFailure = {
  ok: false
  error: 'SnapshotError' | 'FileWriteError' | 'SerializationError' | 'PathValidationError' | 'PermissionError' | 'DiskSpaceError'
  message: string
  cause?: unknown
  filePath?: string
}

export type FileImportSuccess = {
  ok: true
  filePath: string
  bytesRead: number
  prototypesLoaded: number
}

export type FileImportFailure = {
  ok: false
  error: 'FileNotFoundError' | 'FileReadError' | 'ParseError' | 'ValidationError' | 'SetupError' | 'PathValidationError'
  message: string
  cause?: unknown
  filePath?: string
}

export type FileExportResult = FileExportSuccess | FileExportFailure
export type FileImportResult = FileImportSuccess | FileImportFailure

ユースケース

1. 開発用フィクスチャ

import { createPromidasForLocal } from '@f88/promidas'
import { importSnapshotFromFile } from '@f88/promidas-utils/repository'

const repository = createPromidasForLocal('dev-token')

// APIリクエスト不要で固定データをロード
const result = await importSnapshotFromFile(
  repository,
  './fixtures/dev-snapshot.json'
)

if (result.ok) {
  console.log(`✓ Loaded ${result.prototypesLoaded} prototypes`)
}

2. 本番データでのローカル開発

// 本番環境からスナップショットを取得して保存
const prodRepo = createPromidasForServer()
await prodRepo.loadAndStore()

await exportSnapshotToFile(
  prodRepo,
  './data/prod-snapshot-2026-01-13.json',
  { pretty: true }
)

// ローカル開発で本番データを使用
const devRepo = createPromidasForLocal('dev-token')
await importSnapshotFromFile(devRepo, './data/prod-snapshot-2026-01-13.json')

3. バックアップスクリプト

async function createBackup(repository: ProtopediaInMemoryRepository) {
  const timestamp = new Date().toISOString().replace(/:/g, '-')
  const result = await exportSnapshotToFile(
    repository,
    `./backups/snapshot-${timestamp}.json`,
    { pretty: true }
  )
  
  if (result.ok) {
    console.log(`Backup: ${result.filePath} (${result.bytesWritten} bytes)`)
  }
}

実装計画

Phase 1: 基本実装

  • lib/repository/snapshot-file-io.ts を作成
    • exportSnapshotToFile() 実装
    • importSnapshotFromFile() 実装
  • lib/repository/types.ts に Result型を追加
  • @f88/promidas-utils/repository から export

Phase 2: テスト

  • __tests__/snapshot-file-io.test.ts を作成
    • 正常系: export/import のフロー
    • 異常系: ファイルが存在しない、権限エラー、ディスク容量不足など
    • エッジケース: 空のスナップショット、巨大ファイル

Phase 3: ドキュメント

  • docs/api/repository.md を更新
    • 新機能の説明
    • 使用例の追加
  • README.md に使用例を追加

依存関係

promidas のバージョン

  • 最小バージョン: @f88/promidas@^1.1.0
  • 理由: getSerializableSnapshot(), setupSnapshotFromSerializedData() が必要

Node.js API

  • node:fs/promises: ファイル読み書き
  • node:path: パス操作

技術的考慮事項

アトミックな書き込み

// 一時ファイルを使ったアトミックな書き込み
const tempPath = `${filePath}.tmp`
await fs.writeFile(tempPath, json, 'utf-8')
await fs.rename(tempPath, filePath)

エラーハンドリング

  • ファイルシステムエラーを適切に分類
  • 判別可能なエラー型(FileExportFailure.error
  • 元のエラーオブジェクトを cause に保持

ディレクトリの自動作成

await fs.mkdir(dirname(filePath), { recursive: true })

関連リンク

検討項目

  • エラーメッセージの日本語化(既存の parseSnapshotOperationFailure パターンに合わせる?)
  • ファイルパスのバリデーション方法
  • 圧縮オプション(gzip等)の追加
  • 複数ファイルの一括処理機能

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationenhancementNew feature or request

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions