@mapyojojo/vision-shelf-sdk
v0.2.0
Published
Supabase authentication and data synchronization SDK
Readme
@mapyojojo/vision-shelf-sdk (Vision Shelf 共通SDKパッケージ)
@mapyojojo/vision-shelf-sdk は、認証(Supabase Auth)、データベース接続・リアルタイム同期購読、AI(Gemini API)マルチモーダル画像解析ロジックをカプセル化した、ポータブルな共通パッケージ(SDK)です。
接続情報に加え、画像解析エンジンとして Gemini(デフォルト)や Ollama などを注入できます。アプリケーションのコア機能を維持しながら、クラウド、社内LAN、オンプレミスなどの環境に応じて解析先を選択できます。
🚀 導入方法
新しいアプリケーション(例: apps/office-supplies)から本SDKを利用するには、以下の手順でモノレポに追加します。
1. package.json への追加
依存関係に @mapyojojo/vision-shelf-sdk を追加します。
{
"dependencies": {
"@mapyojojo/vision-shelf-sdk": "^0.2.0"
}
}2. ローカルリンクの確立
モノレポのルートで以下を実行し、依存パッケージを解決します。
npm installReact 19 は利用アプリ側でインストールしてください。SDKのHookとコンポーネントはアプリと同じReactインスタンスを使用するため、Reactはpeer dependencyです。
🛠 モジュール構成とAPIリファレンス
本SDKは Next.js などの React Server Components (RSC) 環境での不要な React APIs 混入を防ぐため、サブパスエクスポートを導入しています。
1. @mapyojojo/vision-shelf-sdk (Core / サーバーコンテキスト向け)
サーバーアクション、サーバーコンポーネント、ミドルウェアなど非ブラウザ環境(およびReact依存のないモジュール)向けのエントリポイントです。
サーバーSDKの初期化
import { createServerSdk, OllamaAnalyzer } from "@mapyojojo/vision-shelf-sdk";
// パターンA: 従来通りGemini APIを使用する場合(下位互換性)
const geminiSdk = createServerSdk(
{
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL!,
supabaseAnonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
geminiApiKey: process.env.GEMINI_API_KEY!,
},
{
// Cookieの取得・設定方法を注入 (Next.js 等の環境に準拠)
getAll() {
return myCookieStore.getAll();
},
setAll(cookies, headers) {
cookies.forEach((c) => myCookieStore.set(c.name, c.value, c.options));
// proxy等ではSupabaseが渡すcache-control等のheadersもレスポンスへ転送する
},
},
);
// パターンB: ローカルまたはオンプレミスの Ollama を使用する場合
const ollamaSdk = createServerSdk(
{
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL!,
supabaseAnonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
// Ollama 利用時は Gemini の API キーは不要
aiAnalyzer: new OllamaAnalyzer("http://localhost:11434"),
},
{
// Cookieの取得・設定方法を注入 (Next.js 等の環境に準拠)
getAll() {
return myCookieStore.getAll();
},
setAll(cookies, headers) {
cookies.forEach((c) => myCookieStore.set(c.name, c.value, c.options));
// proxy等ではSupabaseが渡すcache-control等のheadersもレスポンスへ転送する
},
},
);提供サービス
sdk.auth(ServerAuthService):getUser(): セッションユーザーの検証・取得。signIn(email, password): パスワードを用いたサインイン処理。sendPasswordResetEmail(email, redirectTo): パスワードリセット用URLリンクを送信。updateUserPassword(newPassword): ログイン中のユーザーのパスワード変更。
sdk.db(DatabaseService):list<T>(table, options?):orderColumn・ascending・filterColumn・filterValueを指定して取得。insert(table, data): 単一または複数レコードの追加。update(table, id, data): ID指定によるレコードの更新。delete(table, id): ID指定によるレコードの削除。dataに削除したIDの配列を返すため、0件だった場合も判定可能。削除行を返すには対象行の SELECT 権限と RLS による可視性も必要です。
sdk.ai(ImageAnalyzer):analyzeImages<T>({ images, prompt, schema? }): 複数の画像とテキストプロンプトを、注入されたAI解析エンジン(GeminiまたはOllama等)へ送信し、JSONをパースして結果を返します。schemaを指定した場合はsafeParseで検証します。
2. @mapyojojo/vision-shelf-sdk/react (Client / React専用エントリ)
"use client" ディレクティブ下(ブラウザ環境)で React Hook やコンポーネントを利用するためのエントリポイントです。createClientSdk はルートの @mapyojojo/vision-shelf-sdk から、useShelfItems などの Hook と SplashScreen などの React 向け API は @mapyojojo/vision-shelf-sdk/react から import してください。
クライアントSDKの初期化
import { createClientSdk } from "@mapyojojo/vision-shelf-sdk";
export const sdk = createClientSdk({
supabaseUrl: "https://your-project.supabase.co",
supabaseAnonKey: "your-anon-key",
});提供サービス (Client SDK)
sdk.auth(ClientAuthService):getUser(): セッションユーザーの検証・取得。signIn(email, password): パスワードを用いたサインイン処理。signOut(): サインアウト処理。updateUserPassword(newPassword): ログイン中のユーザーのパスワード変更。
sdk.db(DatabaseService): サーバー側と同じlist / insert / update / deleteAPI。
提供Hook
useShelfItems<T, DbT>({ sdk, table, mapDbToModel, orderColumn?, ascending?, filterColumn?, filterValue?, enabled? }):- 引数:
sdk: 初期化済みのVisionShelfClientSdkインスタンス。table: 同期対象のSupabaseテーブル名(例:"seasonings")。mapDbToModel: データベースのオブジェクト構造をアプリケーション用のモデル構造にマッピングする関数。orderColumn: ソートカラム名(デフォルト:"created_at")。ascending: 昇順ソートかどうか(デフォルト:false)。filterColumnとfilterValue: 指定すると、その列の値で取得・Realtime購読を絞り込む。enabled:falseの間は取得・購読を行わない(デフォルト:true)。
- 戻り値:
items: 同期済みのモデル配列(T[])。isLoaded: 初期ロードが完了したかどうかのフラグ(boolean)。refetch: 明示的に手動再取得を行うための非同期関数。error: 取得に失敗した場合のメッセージ。refetchは失敗をthrowせず、この値を更新します。
- 機能:
- コンポーネントのマウント時に初期データを取得。
- 同時に Postgres Changes を用いたRealtime購読チャンネルを開設し、他端末でのデータ登録・変更・削除を瞬時に受信して一覧データを自動同期します。アンマウント時には自動的にチャンネルをクリーンアップします。
- 棚の切り替え後に以前の棚の遅い応答が到着しても、表示を上書きしません。
- 引数:
その他のReact向けAPI
compressImage(file, { maxSize?, quality? }): ブラウザの画像をJPEGへ圧縮し、処理できない場合は元ファイルを返します。useForgotPassword(sendEmail)、useResetCallback(router)、useUpdatePassword(updatePassword, router): パスワード再設定フロー用の共通Hookです。Next.jsのrouterやアプリ固有の送信処理は呼び出し側から渡します。SplashScreen:isLoadedが必須で、appName・subtitle・iconSrc・accentColorを指定できます。
Tailwind CSS v4を利用するアプリでは、SDKのクラスもスキャン対象に追加してください。@source のパスは利用アプリのCSSファイルからの相対パスです。たとえばルート直下の app/globals.css なら次のようにします。
@import "tailwindcss";
@source "../node_modules/@mapyojojo/vision-shelf-sdk/dist/react/**/*.{js}";accentColor に渡すクラスがアプリ側にもSDK側にも静的な文字列として存在しない場合は、アプリ側のCSSで、たとえば @source inline("bg-indigo-600") のように指定してください。
🤖 マルチAIエンジン(Adapterパターン)
本SDKの画像解析機能(sdk.ai)は、Adapterパターンを採用してインターフェースで抽象化されています。これにより、ローカルまたはオンプレミス環境の Ollama などへ解析先を切り替えられます。その場合、画像を外部AIサービスへ送信せずに解析できます。
インターフェース定義 (ImageAnalyzer)
画像解析を行うすべてのエンジンは、以下の ImageAnalyzer インターフェースを実装しています。
import type { ImageInput } from "@mapyojojo/vision-shelf-sdk";
export interface AnalyzeOptions<T = unknown> {
images: (File | ImageInput)[];
prompt: string;
modelName?: string;
schema?: {
safeParse: (data: unknown) => { success: boolean; data?: T; error?: unknown };
}; // データ構造検証用のスキーマ (任意)
}
export interface ImageAnalyzer {
analyzeImages<T>(options: AnalyzeOptions<T>): Promise<{
data?: T;
error?: string;
rawResponse?: string;
}>;
}提供されているAdapterクラスと利用方法
1. GeminiAnalyzer (クラウドAPIモデル)
Google Gemini APIを使用した、クラウドベースの高性能マルチモーダル解析を提供します(初期化方法および必要な環境変数については、上部の【サーバーSDKの初期化(パターンA)】および最下部の環境変数一覧を参照してください)。
2. OllamaAnalyzer (ローカルLLM/オンプレミスモデル)
ローカルまたはオンプレミス環境で起動している Ollama API と通信し、画像を外部AIサービスへ送信せずに解析できます。通信経路や Ollama の運用環境の安全性は、利用側で確認してください(初期化方法と環境変数は、上部の「サーバーSDKの初期化(パターンB)」および下部の環境変数一覧を参照)。
🔒 セキュリティ機能と対策
1. safeParse互換スキーマによるAI解析データの検証
schema オプションに safeParse(data: unknown) 互換のスキーマを渡すと、JSONパース後のAI解析データがアプリケーション側の予期する構造(例:KitData[] や SeasoningData[])に合致するか検証します。Zodは利用できるスキーマの一例であり、SDKのインターフェースはZod固有ではありません。schema を省略した場合、構造の検証は行いません。
JSONのパースまたは指定したスキーマによる検証に失敗した場合、解析結果として { error: "AI解析データのスキーマ検証に失敗しました。" } を返します。スキーマ検証は、プロンプトインジェクションそのものを防ぐものではありません。
- 利用例 (スキーマ検証の有効化):
import { z } from "zod"; // スキーマ定義 const itemSchema = z.object({ name: z.string().nullable(), quantity: z.number(), }); const itemListSchema = z.array(itemSchema); // 解析実行 (schemaオプションを渡す) const result = await sdk.ai.analyzeImages({ images: [imageFile], prompt: "画像を解析してアイテム一覧をJSONで返して", schema: itemListSchema, // safeParse 互換スキーマを指定 }); if (result.error) { console.error(result.error); // スキーマ検証に失敗した場合はエラーが返る } else { console.log(result.data); // 型安全に検証された配列 }
2. SSRF (Server-Side Request Forgery) リスクの緩和と例外設定
ローカルLLMへの切り替えに対応する OllamaAnalyzer では、エンドポイントへの不正なリクエスト送信による SSRF 脆弱性を緩和するための検知ロジックを実装しています。
接続先アドレスの制限: 本番環境(
process.env.NODE_ENV === "production")では、OllamaAnalyzerの接続先ホスト名がlocalhost、IPv4ループバック(127.0.0.0/8)、IPv6ループバック(::1)、IPv4リンクローカル(169.254.0.0/16)、プライベートIPv4(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)、またはfc00:・fd00:・fe80:で始まるIPv6アドレスに該当すると、コンストラクタで例外を投げます。これは接続先URLのホスト名に対するチェックであり、SSRFリスクを完全に排除するものではありません。ALLOW_PRIVATE_NETWORKSによる例外設定: 本番環境で社内ネットワーク上の Ollama に接続する場合は、環境変数ALLOW_PRIVATE_NETWORKS=trueを設定すると、上記の接続先アドレス制限を解除できます。この設定はSSRFリスクを緩和する保護を一部解除するため、接続先とネットワークのアクセス制御を利用側で確認してください。環境変数一覧: | 環境変数 | 設定値 | 説明 | | :------------------------------ | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------ | |
NEXT_PUBLIC_SUPABASE_URL| URL文字列 | SupabaseプロジェクトのAPI URL(SDKの共通基盤として必須) | |NEXT_PUBLIC_SUPABASE_ANON_KEY| JWT文字列 | Supabaseのクライアント用匿名キー(RLSが強制される安全なキー、SDKの共通基盤として必須) | |GEMINI_API_KEY| APIキー文字列 | Gemini APIを利用してクラウドベースの画像解析を行う場合に必須(パターンA用) | |ALLOW_PRIVATE_NETWORKS|true| 本番環境でローカル・プライベートアドレスへの接続制限を解除する設定。SSRFリスクを緩和する保護が一部解除される(パターンB用) |
