Agent Skills by ALSEL
Anthropic Claudeその他⭐ リポ 0品質スコア 50/100

prisma-driver-adapter-implementation

Prisma v7のドライバーアダプター実装に必須のリファレンス。アダプターの実装・修正、データベースドライバーの追加、`SqlDriverAdapter`/`Transaction`インターフェースに関わる作業を行う際に使用する。トランザクションのライフサイクルプロトコルやエラーマッピング要件、検証チェックリストなど、コード例からは推測できない重要な仕様を含んでおり、既存の実装があっても本スキルは省略不可。

description の原文を見る

Required reference for Prisma v7 driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter/Transaction interfaces. Contains critical contract details not inferable from code examples — including the transaction lifecycle protocol, error mapping requirements, and verification checklist. Existing implementations do not replace this skill.

SKILL.md 本文

Prisma 7 ドライバアダプタ実装ガイド

このスキルは、任意のデータベース向け Prisma ORM v7 ドライバアダプタを実装するために必要なすべての情報を提供します。

アーキテクチャ概要

┌─────────────────────────────────────────────────────────────────┐
│                         PrismaClient                            │
│                    (requires adapter factory)                   │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│            SqlMigrationAwareDriverAdapterFactory                │
│   ┌─────────────────────┐    ┌─────────────────────────────┐    │
│   │ connect()           │    │ connectToShadowDb()         │    │
│   │ → SqlDriverAdapter  │    │ → SqlDriverAdapter          │    │
│   └─────────────────────┘    └─────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                      SqlDriverAdapter                           │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│  │ queryRaw()   │ │ executeRaw() │ │ startTransaction()       │ │
│  │ → ResultSet  │ │ → number     │ │ → Transaction            │ │
│  └──────────────┘ └──────────────┘ └──────────────────────────┘ │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│  │executeScript │ │ dispose()    │ │ getConnectionInfo()      │ │
│  └──────────────┘ └──────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                        Transaction                              │
│  Extends SqlQueryable + commit() + rollback() + options         │
│  (lifecycle hooks only — Prisma sends SQL via executeRaw)       │
└─────────────────────────────────────────────────────────────────┘

必須インターフェース

@prisma/driver-adapter-utils からインポートします:

import type {
  ColumnType,
  IsolationLevel,
  SqlDriverAdapter,
  SqlMigrationAwareDriverAdapterFactory,
  SqlQuery,
  SqlQueryable,
  SqlResultSet,
  Transaction,
  TransactionOptions,
  ArgType,
  ConnectionInfo,
  MappedError,
} from "@prisma/driver-adapter-utils";
import {
  ColumnTypeEnum,
  DriverAdapterError,
} from "@prisma/driver-adapter-utils";

インターフェース定義

SqlQuery (queryRaw/executeRaw への入力)

type SqlQuery = {
  sql: string; // Parameterized SQL with placeholders
  args: Array<unknown>; // Bound parameter values
  argTypes: Array<ArgType>; // Type hints for each argument
};

type ArgType = {
  scalarType: ArgScalarType; // 'string' | 'int' | 'bigint' | 'float' | 'decimal' | 'boolean' | 'enum' | 'uuid' | 'json' | 'datetime' | 'bytes' | 'unknown'
  dbType?: string;
  arity: "scalar" | "list";
};

SqlResultSet (queryRaw からの出力)

interface SqlResultSet {
  columnNames: Array<string>; // Column names in order
  columnTypes: Array<ColumnType>; // Column types matching columnNames
  rows: Array<Array<unknown>>; // Row data as arrays
  lastInsertId?: string; // For INSERT without RETURNING
}

ColumnTypeEnum の値

const ColumnTypeEnum = {
  Int32: 0,
  Int64: 1,
  Float: 2,
  Double: 3,
  Numeric: 4,
  Boolean: 5,
  Character: 6,
  Text: 7,
  Date: 8,
  Time: 9,
  DateTime: 10,
  Json: 11,
  Enum: 12,
  Bytes: 13,
  Set: 14,
  Uuid: 15,
  Int32Array: 64,
  Int64Array: 65,
  FloatArray: 66,
  DoubleArray: 67,
  NumericArray: 68,
  BooleanArray: 69,
  CharacterArray: 70,
  TextArray: 71,
  DateArray: 72,
  TimeArray: 73,
  DateTimeArray: 74,
  JsonArray: 75,
  EnumArray: 76,
  BytesArray: 77,
  UuidArray: 78,
  UnknownNumber: 128,
} as const;

SqlDriverAdapter

interface SqlDriverAdapter extends SqlQueryable {
  executeScript(script: string): Promise<void>;
  startTransaction(isolationLevel?: IsolationLevel): Promise<Transaction>;
  getConnectionInfo?(): ConnectionInfo;
  dispose(): Promise<void>;
}

Transaction

interface Transaction extends SqlQueryable {
  readonly options: TransactionOptions;
  commit(): Promise<void>;
  rollback(): Promise<void>;
}

type TransactionOptions = { usePhantomQuery: boolean };

SqlMigrationAwareDriverAdapterFactory

interface SqlMigrationAwareDriverAdapterFactory {
  readonly provider: "mysql" | "postgres" | "sqlite" | "sqlserver";
  readonly adapterName: string;
  connect(): Promise<SqlDriverAdapter>;
  connectToShadowDb(): Promise<SqlDriverAdapter>;
}

実装ステップ

ステップ 1: Queryable ベースクラスを作成

class MyQueryable<TClient> implements SqlQueryable {
  readonly provider = "postgres" as const; // or 'sqlite' | 'mysql' | 'sqlserver'
  readonly adapterName = "@my-org/adapter-mydb" as const;

  constructor(protected readonly client: TClient) {}

  async queryRaw(query: SqlQuery): Promise<SqlResultSet> {
    try {
      const args = query.args.map((arg, i) =>
        mapArg(arg, query.argTypes[i] ?? { scalarType: "unknown", arity: "scalar" })
      );

      // Execute query with your driver
      const result = await this.client.query(query.sql, args);

      // Extract column metadata
      const columnNames = /* get from result */;
      const columnTypes = /* map to ColumnTypeEnum */;

      // Map rows to ResultValue arrays
      const rows = result.map(row => mapRow(row, columnTypes));

      return { columnNames, columnTypes, rows };
    } catch (e) {
      this.onError(e);
    }
  }

  async executeRaw(query: SqlQuery): Promise<number> {
    try {
      const args = query.args.map((arg, i) =>
        mapArg(arg, query.argTypes[i] ?? { scalarType: "unknown", arity: "scalar" })
      );
      const result = await this.client.query(query.sql, args);
      return result.affectedRows ?? 0;
    } catch (e) {
      this.onError(e);
    }
  }

  protected onError(error: unknown): never {
    throw new DriverAdapterError(convertDriverError(error));
  }
}

ステップ 2: Transaction クラスを作成

重要: commit()rollback()ライフサイクルフックのみです。SQL を実行してはいけません。Prisma はトランザクションオブジェクトの executeRaw 経由で COMMIT/ROLLBACK を送信します。

class MyTransaction extends MyQueryable<TClient> implements Transaction {
  readonly options: TransactionOptions;
  readonly #release: () => void;

  constructor(
    client: TClient,
    options: TransactionOptions,
    release: () => void,
  ) {
    super(client);
    this.options = options;
    this.#release = release;
  }

  commit(): Promise<void> {
    // DO NOT issue COMMIT SQL here — Prisma does it via executeRaw
    this.#release(); // Release connection/resources
    return Promise.resolve();
  }

  rollback(): Promise<void> {
    // DO NOT issue ROLLBACK SQL here — Prisma does it via executeRaw
    this.#release();
    return Promise.resolve();
  }
}

ステップ 3: Adapter クラスを作成

class MyAdapter extends MyQueryable<TClient> implements SqlDriverAdapter {
  #transactionDepth = 0;

  constructor(client: TClient) {
    super(client);
  }

  async executeScript(script: string): Promise<void> {
    // For SQLite: split on ';' and run each statement
    // For Postgres: use multi-statement execution
    try {
      // Implementation depends on driver capabilities
    } catch (e) {
      this.onError(e);
    }
  }

  async startTransaction(
    isolationLevel?: IsolationLevel,
  ): Promise<Transaction> {
    // Validate isolation level for your database
    const validLevels = new Set<IsolationLevel>([
      "READ UNCOMMITTED",
      "READ COMMITTED",
      "REPEATABLE READ",
      "SERIALIZABLE",
    ]);

    if (isolationLevel !== undefined && !validLevels.has(isolationLevel)) {
      throw new DriverAdapterError({
        kind: "InvalidIsolationLevel",
        level: isolationLevel,
      });
    }

    const options: TransactionOptions = { usePhantomQuery: false };

    this.#transactionDepth += 1;
    const depth = this.#transactionDepth;

    try {
      if (depth === 1) {
        // Issue BEGIN (with isolation level if specified)
        const beginSql = isolationLevel
          ? `BEGIN ISOLATION LEVEL ${isolationLevel}`
          : "BEGIN";
        await this.client.query(beginSql);
      } else {
        // Nested: use savepoints
        await this.client.query(`SAVEPOINT sp_${depth}`);
      }
    } catch (e) {
      this.#transactionDepth -= 1;
      this.onError(e);
    }

    const release = () => {
      this.#transactionDepth -= 1;
    };
    return new MyTransaction(this.client, options, release);
  }

  getConnectionInfo(): ConnectionInfo {
    return { supportsRelationJoins: true };
  }

  async dispose(): Promise<void> {
    await this.client.close();
  }
}

ステップ 4: Factory クラスを作成

export type MyAdapterConfig = {
  url: string;
};

export type MyAdapterOptions = {
  shadowDatabaseUrl?: string;
};

export class MyAdapterFactory implements SqlMigrationAwareDriverAdapterFactory {
  readonly provider = "postgres" as const;
  readonly adapterName = "@my-org/adapter-mydb" as const;

  constructor(
    private readonly config: MyAdapterConfig,
    private readonly options?: MyAdapterOptions,
  ) {}

  connect(): Promise<SqlDriverAdapter> {
    return Promise.resolve(new MyAdapter(openConnection(this.config.url)));
  }

  connectToShadowDb(): Promise<SqlDriverAdapter> {
    const url = this.options?.shadowDatabaseUrl ?? this.config.url;
    return Promise.resolve(new MyAdapter(openConnection(url)));
  }
}

変換ヘルパー

引数マッピング (入力)

Prisma の引数値をドライバのネイティブ型に変換します:

function mapArg(arg: unknown, argType: ArgType): unknown {
  if (arg === null || arg === undefined) return null;

  // String → number for int columns
  if (typeof arg === "string" && argType.scalarType === "int")
    return Number.parseInt(arg, 10);

  // String → number for float columns
  if (typeof arg === "string" && argType.scalarType === "float")
    return Number.parseFloat(arg);

  // String → BigInt for bigint columns
  if (typeof arg === "string" && argType.scalarType === "bigint")
    return BigInt(arg);

  // Base64 string → Buffer for bytes columns
  if (typeof arg === "string" && argType.scalarType === "bytes")
    return Buffer.from(arg, "base64");

  // Boolean → 0/1 for SQLite
  if (typeof arg === "boolean" && /* SQLite */)
    return arg ? 1 : 0;

  return arg;
}

行マッピング (出力)

ドライバ結果値を Prisma が期待する型に変換します:

function mapRow(row: unknown[], columnTypes: ColumnType[]): ResultValue[] {
  const result: ResultValue[] = [];

  for (let i = 0; i < row.length; i++) {
    const value = row[i] ?? null;
    const colType = columnTypes[i];

    if (value === null) {
      result.push(null);
      continue;
    }

    // bigint → string for Int64 (JSON-safe)
    if (typeof value === "bigint") {
      result.push(value.toString());
      continue;
    }

    // Date → ISO 8601 string for DateTime
    if (value instanceof Date) {
      result.push(value.toISOString());
      continue;
    }

    // JSON objects → stringified
    if (colType === ColumnTypeEnum.Json && typeof value === "object") {
      result.push(JSON.stringify(value));
      continue;
    }

    result.push(value as ResultValue);
  }

  return result;
}

列型推論

ドライバが型メタデータを提供しない場合、JS 値から推論します:

function inferColumnType(value: NonNullable<unknown>): ColumnType {
  if (typeof value === "boolean") return ColumnTypeEnum.Boolean;
  if (typeof value === "bigint") return ColumnTypeEnum.Int64;
  if (value instanceof Uint8Array) return ColumnTypeEnum.Bytes;
  if (value instanceof Date) return ColumnTypeEnum.DateTime;
  if (Array.isArray(value)) return ColumnTypeEnum.Text; // fallback
  if (typeof value === "object") return ColumnTypeEnum.Json;
  if (typeof value === "number") return ColumnTypeEnum.UnknownNumber;
  return ColumnTypeEnum.Text;
}

エラーハンドリング

ドライバエラーを MappedError にマップして、Prisma が正しく処理できるようにします:

function convertDriverError(error: unknown): MappedError {
  if (error instanceof Error) {
    // Database-specific error mapping
    const dbError = error as Error & { code?: string; errno?: number };

    // PostgreSQL example
    if (dbError.code === "23505") {
      return { kind: "UniqueConstraintViolation" };
    }
    if (dbError.code === "23502") {
      return { kind: "NullConstraintViolation" };
    }
    if (dbError.code === "23503") {
      return { kind: "ForeignKeyConstraintViolation" };
    }
    if (dbError.code === "42P01") {
      return { kind: "TableDoesNotExist" };
    }

    // SQLite example
    if (error.name === "SQLiteError") {
      return {
        kind: "sqlite",
        extendedCode: dbError.errno ?? 1,
        message: error.message,
      };
    }

    // PostgreSQL raw error
    if (dbError.code) {
      return {
        kind: "postgres",
        code: dbError.code,
        severity: "ERROR",
        message: error.message,
        detail: undefined,
        column: undefined,
        hint: undefined,
      };
    }
  }

  return { kind: "GenericJs", id: 0 };
}

データベース固有の注記

SQLite

  • データベースを開くときに safeIntegers: true を設定して、大きな整数に対して bigint を取得します
  • SERIALIZABLE 分離レベルのみが有効です
  • executeScript: ; で分割して各ステートメントを個別に実行します
  • ブール値: 0/1 として保存し、ブール値として返します

PostgreSQL

  • すべての標準分離レベルが有効です
  • コネクションプーリング (PgBouncer) の場合、prepare: false を使用します
  • トランザクションは専用接続が必要です (reserve() パターン)
  • executeScript: マルチステートメント実行を使用します (一部ドライバの .simple())
  • int8 列は文字列として返される場合があります (既にドライバによって文字列化されています)
  • numeric 列は精度を保つために文字列として返されます

MySQL/MariaDB

  • READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE をサポートします
  • パラメータに ? プレースホルダを使用します
  • 大きな値の BIGINT を文字列として処理します

テスト戦略

ユニットテスト (PrismaClient なし)

アダプタを生のデータベースドライバで直接テストします:

describe("queryRaw", () => {
  test("returns column names and types", async () => {
    const adapter = new MyAdapter(createTestConnection());
    const result = await adapter.queryRaw({
      sql: "SELECT id, name FROM users",
      args: [],
      argTypes: [],
    });
    expect(result.columnNames).toEqual(["id", "name"]);
    expect(result.columnTypes[0]).toBe(ColumnTypeEnum.Int32);
  });
});

describe("startTransaction", () => {
  test("commit persists changes", async () => {
    const adapter = new MyAdapter(createTestConnection());
    const tx = await adapter.startTransaction();
    await tx.executeRaw({
      sql: "INSERT INTO users (name) VALUES (?)",
      args: ["Alice"],
      argTypes: [],
    });
    // Prisma sends COMMIT via executeRaw
    await tx.executeRaw({ sql: "COMMIT", args: [], argTypes: [] });
    await tx.commit(); // lifecycle hook only
    // Verify data persisted
  });
});

E2E テスト (PrismaClient 使用)

完全な統合をテストします:

describe("E2E", () => {
  let prisma: PrismaClient;

  beforeEach(async () => {
    const factory = new MyAdapterFactory({ url: TEST_DB_URL });
    prisma = new PrismaClient({ adapter: factory });
  });

  test("CRUD operations", async () => {
    const user = await prisma.user.create({ data: { name: "Alice" } });
    expect(user.id).toBeGreaterThan(0);

    const found = await prisma.user.findUnique({ where: { id: user.id } });
    expect(found?.name).toBe("Alice");
  });

  test("transactions roll back on error", async () => {
    await expect(
      prisma.$transaction(async (tx) => {
        await tx.user.create({ data: { name: "Bob" } });
        throw new Error("Rollback!");
      }),
    ).rejects.toThrow();

    expect(await prisma.user.count()).toBe(0);
  });
});

使用例

import { PrismaClient } from "./generated/prisma/client";
import { MyAdapterFactory } from "@my-org/adapter-mydb";

const factory = new MyAdapterFactory({
  url: process.env.DATABASE_URL!,
});

const prisma = new PrismaClient({ adapter: factory });

// Use prisma normally
const users = await prisma.user.findMany();

チェックリスト

アダプタを完了したと判断する前に:

  • SqlMigrationAwareDriverAdapterFactoryconnect()connectToShadowDb() で実装されている
  • SqlDriverAdapterqueryRawexecuteRawexecuteScriptstartTransactiondispose を実装している
  • TransactionqueryRawexecuteRawcommitrollbackoptions: { usePhantomQuery: false } で実装している
  • commit()rollback() がライフサイクルフックのみ (SQL を発行していない)
  • startTransactionBEGIN (深さ 1) または SAVEPOINT sp_N (ネスト) を発行している
  • 引数マッピングが以下を処理している: string→int、string→bigint、string→float、base64→bytes
  • 行マッピングが以下を処理している: bigint→string、Date→ISO string、JSON→string
  • 列型が ColumnTypeEnum に正しくマップされている
  • エラーが DriverAdapterError でラップされて、適切な MappedError kind が設定されている
  • ターゲットデータベースの分離レベル検証
  • queryRaw、executeRaw、executeScript、トランザクションのユニットテストが成功している
  • 実際の PrismaClient を使用した E2E テストが成功している

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
prisma
リポジトリ
prisma/skills
ライセンス
MIT
最終更新
不明

Source: https://github.com/prisma/skills / ライセンス: MIT

関連スキル

汎用その他⭐ リポ 1,982

superfluid

Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper

by LeoYeAI
汎用その他⭐ リポ 100

civ-finish-quotes

実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。

by huxiuhan
汎用その他⭐ リポ 1,110

nookplot

Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。

by BankrBot
汎用その他⭐ リポ 59

web3-polymarket

Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。

by elophanto
汎用その他⭐ リポ 52

ethskills

Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。

by jiayaoqijia
汎用その他⭐ リポ 44

xxyy-trade

このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。

by Jimmy-Holiday
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: prisma · prisma/skills · ライセンス: MIT