npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

ai-girlfriend-shared-lib

v1.0.2

Published

Shared library for AI girlfriend chat workers

Readme

共有ライブラリ(shared-lib)仕様書

🎯 概要

全Workerで使用する共通ライブラリ

R2認証ゲートウェイ経由でのセキュアなR2アクセスと、共通ユーティリティを提供します。

📚 提供ライブラリ

1. SecureR2Client

R2認証ゲートウェイ経由でのR2アクセスクライアント

基本使用法

import { SecureR2Client } from '../shared-lib/secure-r2-client.js';

const r2 = new SecureR2Client(
  'auth-worker',                    // Worker名
  env.AUTH_WORKER_R2_KEY,          // 認証キー
  env.R2_GATEWAY_URL               // ゲートウェイURL
);

// ユーザーデータアクセス
const profile = await r2.getUserData('user_123', 'profile.json');
await r2.putUserData('user_123', 'profile.json', profileData);
await r2.deleteUserData('user_123', 'old_data.json');

// 共有データアクセス
const sharedData = await r2.getSharedData('gallery/character_456.json');
await r2.putSharedData('templates/template_789.json', templateData);

2. 共通ユーティリティ

  • バリデーション: メール、パスワード、ID等の検証
  • 暗号化: パスワードハッシュ、JWT処理
  • エラーハンドリング: 統一エラー形式
  • ログ処理: 構造化ログ出力

🔧 SecureR2Client 詳細仕様

初期化

new SecureR2Client(workerName, authKey, gatewayUrl, options)

パラメータ:

  • workerName: 呼び出し元Worker名(必須)
  • authKey: Worker固有の認証キー(必須)
  • gatewayUrl: R2認証ゲートウェイのURL(必須)
  • options: オプション設定(オプション)

メソッド一覧

ユーザーデータ操作

// GET: ユーザーデータ取得
async getUserData(userId, path, options = {})
// 例: await r2.getUserData('user_123', 'profile.json')

// PUT: ユーザーデータ保存
async putUserData(userId, path, data, options = {})
// 例: await r2.putUserData('user_123', 'profile.json', profileData)

// DELETE: ユーザーデータ削除
async deleteUserData(userId, path, options = {})
// 例: await r2.deleteUserData('user_123', 'old_session.json')

// LIST: ユーザーデータ一覧
async listUserData(userId, prefix = '', options = {})
// 例: await r2.listUserData('user_123', 'characters/')

共有データ操作

// GET: 共有データ取得
async getSharedData(path, options = {})
// 例: await r2.getSharedData('gallery/character_456.json')

// PUT: 共有データ保存
async putSharedData(path, data, options = {})
// 例: await r2.putSharedData('templates/new_template.json', data)

// DELETE: 共有データ削除
async deleteSharedData(path, options = {})
// 例: await r2.deleteSharedData('gallery/old_character.json')

// LIST: 共有データ一覧
async listSharedData(prefix = '', options = {})
// 例: await r2.listSharedData('gallery/')

低レベルAPI

// 直接パス指定でのアクセス
async get(path, options = {})
async put(path, data, options = {})
async delete(path, options = {})
async list(prefix = '', options = {})

パス自動生成

各Workerに応じて適切なパスプレフィックスを自動生成:

const PATH_PREFIXES = {
  'auth-worker': 'auth',
  'character-worker': 'characters',
  'chat-worker': 'conversations',
  'media-worker': 'images',
  'billing-worker': 'billing'
};

// 例: auth-workerで getUserData('user_123', 'profile.json')
// → 実際のパス: 'auth/users/user_123/profile.json'

エラーハンドリング

try {
  const data = await r2.getUserData('user_123', 'profile.json');
} catch (error) {
  if (error instanceof R2AuthError) {
    // 認証エラー
    console.error('Authentication failed:', error.message);
  } else if (error instanceof R2NotFoundError) {
    // データが存在しない
    console.log('Profile not found for user_123');
  } else if (error instanceof R2ForbiddenError) {
    // アクセス権限なし
    console.error('Access denied:', error.message);
  } else {
    // その他のエラー
    console.error('R2 operation failed:', error.message);
  }
}

レスポンス形式

成功時

// GET操作
{
  success: true,
  data: {...},           // JSONデータ(自動パース済み)
  metadata: {
    etag: "abc123",
    size: 1024,
    lastModified: "2024-01-15T10:00:00Z"
  }
}

// PUT操作
{
  success: true,
  path: "auth/users/user_123/profile.json",
  etag: "def456",
  size: 512
}

// DELETE操作
{
  success: true,
  path: "auth/users/user_123/old_data.json",
  deleted: true
}

// LIST操作
{
  success: true,
  objects: [
    {
      key: "auth/users/user_123/profile.json",
      size: 512,
      lastModified: "2024-01-15T10:00:00Z"
    }
  ],
  truncated: false
}

エラー時

{
  success: false,
  error: "Access denied to path: characters/users/user_123/",
  code: "FORBIDDEN",
  workerName: "auth-worker",
  path: "characters/users/user_123/character.json",
  requestId: "req_abc123"
}

🛠️ 共通ユーティリティ

ValidationUtils

import { ValidationUtils } from '../shared-lib/validation-utils.js';

// メールアドレス検証
ValidationUtils.validateEmail('[email protected]'); // true

// パスワード検証
ValidationUtils.validatePassword('SecurePass123!'); // true

// ユーザーID検証
ValidationUtils.validateUserId('user_123'); // true

// 複合バリデーション
const result = ValidationUtils.validateUserRegistration({
  email: '[email protected]',
  password: 'SecurePass123!',
  name: '山田太郎'
});
// { valid: true, errors: [] }

CryptoUtils

import { CryptoUtils } from '../shared-lib/crypto-utils.js';

// パスワードハッシュ化
const hashedPassword = await CryptoUtils.hashPassword('plaintext');

// パスワード検証
const isValid = await CryptoUtils.verifyPassword('plaintext', hashedPassword);

// JWT生成
const token = await CryptoUtils.generateJWT({
  userId: 'user_123',
  exp: Date.now() + 86400000
}, secret);

// JWT検証
const payload = await CryptoUtils.verifyJWT(token, secret);

LogUtils

import { LogUtils } from '../shared-lib/log-utils.js';

// 構造化ログ
LogUtils.info('User registered', {
  userId: 'user_123',
  email: '[email protected]',
  timestamp: Date.now()
});

// エラーログ
LogUtils.error('R2 access failed', {
  workerName: 'auth-worker',
  path: 'auth/users/user_123/profile.json',
  error: error.message
});

// パフォーマンスログ
LogUtils.perf('R2 operation', {
  operation: 'GET',
  path: 'auth/users/user_123/profile.json',
  duration: 45
});

ErrorUtils

import { ErrorUtils } from '../shared-lib/error-utils.js';

// 統一エラーレスポンス生成
const errorResponse = ErrorUtils.createErrorResponse(
  400,
  'VALIDATION_ERROR',
  'Invalid email format',
  { field: 'email' }
);

// エラー分類
if (ErrorUtils.isRetryableError(error)) {
  // リトライ可能なエラー
  await retryOperation();
}

// エラーログ出力
ErrorUtils.logError(error, {
  workerName: 'auth-worker',
  operation: 'getUserData',
  context: { userId: 'user_123' }
});

📋 設定とカスタマイズ

デフォルト設定

const DEFAULT_OPTIONS = {
  timeout: 10000,        // 10秒タイムアウト
  retries: 3,            // 3回リトライ
  retryDelay: 1000,      // 1秒リトライ間隔
  cacheEnabled: true,    // レスポンスキャッシュ有効
  cacheTTL: 300,         // 5分キャッシュ
  logLevel: 'info'       // ログレベル
};

Worker別カスタマイズ

// Worker固有の設定上書き
const r2 = new SecureR2Client('auth-worker', authKey, gatewayUrl, {
  timeout: 5000,          // 認証は高速にしたい
  retries: 1,             // 認証エラーはリトライしない
  cacheEnabled: false     // 認証データはキャッシュしない
});

🧪 テスト用モック

モッククライアント

import { MockR2Client } from '../shared-lib/mock-r2-client.js';

// テスト環境用
const r2 = new MockR2Client();

// インメモリデータで動作
await r2.putUserData('user_123', 'profile.json', mockProfile);
const profile = await r2.getUserData('user_123', 'profile.json');

テストヘルパー

import { TestHelpers } from '../shared-lib/test-helpers.js';

// テストデータ生成
const mockUser = TestHelpers.createMockUser();
const mockCharacter = TestHelpers.createMockCharacter();

// R2モック設定
TestHelpers.setupR2Mock({
  'auth/users/user_123/profile.json': mockUser,
  'characters/users/user_123/characters/char_456.json': mockCharacter
});

📦 パッケージ構成

shared-lib/
├── src/
│   ├── secure-r2-client.js      # メインクライアント
│   ├── validation-utils.js      # バリデーション
│   ├── crypto-utils.js          # 暗号化処理
│   ├── log-utils.js             # ログ処理
│   ├── error-utils.js           # エラー処理
│   ├── test-helpers.js          # テストヘルパー
│   └── mock-r2-client.js        # モッククライアント
├── tests/
│   ├── secure-r2-client.test.js
│   ├── validation-utils.test.js
│   ├── crypto-utils.test.js
│   └── integration.test.js
├── package.json
└── README.md

🔄 使用例(Worker別)

Auth Worker

const r2 = new SecureR2Client('auth-worker', env.AUTH_WORKER_R2_KEY, env.R2_GATEWAY_URL);

// ユーザープロファイル操作
const profile = await r2.getUserData(userId, 'profile.json');
await r2.putUserData(userId, 'profile.json', updatedProfile);

// セッション管理
await r2.putUserData(userId, `sessions/${sessionId}.json`, sessionData);
const sessions = await r2.listUserData(userId, 'sessions/');

Character Worker

const r2 = new SecureR2Client('character-worker', env.CHARACTER_WORKER_R2_KEY, env.R2_GATEWAY_URL);

// キャラクター管理
const character = await r2.getUserData(userId, `characters/${characterId}.json`);
await r2.putUserData(userId, `characters/${characterId}.json`, characterData);

// ギャラリー操作
await r2.putSharedData(`gallery/${sharedId}.json`, sharedCharacter);
const gallery = await r2.listSharedData('gallery/');

Chat Worker

const r2 = new SecureR2Client('chat-worker', env.CHAT_WORKER_R2_KEY, env.R2_GATEWAY_URL);

// 会話履歴
const conversation = await r2.getUserData(userId, `conversations/${characterId}/${conversationId}.json`);
await r2.putUserData(userId, `conversations/${characterId}/${conversationId}.json`, conversationData);

// キャラクター情報(読み取り専用)
const character = await r2.get(`characters/users/${userId}/characters/${characterId}.json`);

この共有ライブラリにより、全Workerで統一された安全なR2アクセスが実現できます。