[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71NZZMQTCX4YPC2SPJJGRM1":3,"skill-same:01M71NZZMQTCX4YPC2SPJJGRM1":75},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":52,"commitSha":53,"skillDirectory":54,"forksCount":55,"repositoryHtmlUrl":56,"repositoryHomepageUrl":6,"ownerHtmlUrl":57,"ownerBlogUrl":58,"ownerDescription":59,"updatedAt":60,"install":61,"resources":74},"01M71NZZMQTCX4YPC2SPJJGRM1","prisma-driver-adapter-implementation",null,"Required reference for Prisma v7 driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter\u002FTransaction 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.","iDorgham\u002FWorkspace-Factory","iDorgham","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F136521493?v=4","01M6CNGBDAHE4ARW3YJEF4X2FD","Dorgham","01M71E8H3CXZ7DJX59MTM9JEB2",1,"MIT","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_prisma_prisma_driver_adapter_implementation\u002Fskill.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"adapter","prisma driver adapter implementation","implementation","driver","prisma","Existing","use","contract","lifecycle","for","do","mapping","work","verification","code","when","database","critical","implementing","error","2026-11-10 10:56:23",false,"---\nname: prisma-driver-adapter-implementation\ndescription: Required reference for Prisma v7 driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter\u002FTransaction 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.\nlicense: MIT\nmetadata:\n  author: Tyler Benfield\n  version: \"7.6.0\"\n---\n\n# Prisma 7 Driver Adapter Implementation Guide\n\nThis skill provides everything needed to implement a Prisma ORM v7 driver adapter for any database.\n\n## Architecture Overview\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                         PrismaClient                            │\n│                    (requires adapter factory)                   │\n└─────────────────────────────────────────────────────────────────┘\n                                │\n                                ▼\n┌─────────────────────────────────────────────────────────────────┐\n│            SqlMigrationAwareDriverAdapterFactory                │\n│   ┌─────────────────────┐    ┌─────────────────────────────┐    │\n│   │ connect()           │    │ connectToShadowDb()         │    │\n│   │ → SqlDriverAdapter  │    │ → SqlDriverAdapter          │    │\n│   └─────────────────────┘    └─────────────────────────────┘    │\n└─────────────────────────────────────────────────────────────────┘\n                                │\n                                ▼\n┌─────────────────────────────────────────────────────────────────┐\n│                      SqlDriverAdapter                           │\n│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │\n│  │ queryRaw()   │ │ executeRaw() │ │ startTransaction()       │ │\n│  │ → ResultSet  │ │ → number     │ │ → Transaction            │ │\n│  └──────────────┘ └──────────────┘ └──────────────────────────┘ │\n│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │\n│  │executeScript │ │ dispose()    │ │ getConnectionInfo()      │ │\n│  └──────────────┘ └──────────────┘ └──────────────────────────┘ │\n└─────────────────────────────────────────────────────────────────┘\n                                │\n                                ▼\n┌─────────────────────────────────────────────────────────────────┐\n│                        Transaction                              │\n│  Extends SqlQueryable + commit() + rollback() + options         │\n│  (lifecycle hooks only — Prisma sends SQL via executeRaw)       │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Required Interfaces\n\nImport from `@prisma\u002Fdriver-adapter-utils`:\n\n```typescript\nimport type {\n  ColumnType,\n  IsolationLevel,\n  SqlDriverAdapter,\n  SqlMigrationAwareDriverAdapterFactory,\n  SqlQuery,\n  SqlQueryable,\n  SqlResultSet,\n  Transaction,\n  TransactionOptions,\n  ArgType,\n  ConnectionInfo,\n  MappedError,\n} from \"@prisma\u002Fdriver-adapter-utils\";\nimport {\n  ColumnTypeEnum,\n  DriverAdapterError,\n} from \"@prisma\u002Fdriver-adapter-utils\";\n```\n\n## Interface Definitions\n\n### SqlQuery (input to queryRaw\u002FexecuteRaw)\n\n```typescript\ntype SqlQuery = {\n  sql: string; \u002F\u002F Parameterized SQL with placeholders\n  args: Array\u003Cunknown>; \u002F\u002F Bound parameter values\n  argTypes: Array\u003CArgType>; \u002F\u002F Type hints for each argument\n};\n\ntype ArgType = {\n  scalarType: ArgScalarType; \u002F\u002F 'string' | 'int' | 'bigint' | 'float' | 'decimal' | 'boolean' | 'enum' | 'uuid' | 'json' | 'datetime' | 'bytes' | 'unknown'\n  dbType?: string;\n  arity: \"scalar\" | \"list\";\n};\n```\n\n### SqlResultSet (output from queryRaw)\n\n```typescript\ninterface SqlResultSet {\n  columnNames: Array\u003Cstring>; \u002F\u002F Column names in order\n  columnTypes: Array\u003CColumnType>; \u002F\u002F Column types matching columnNames\n  rows: Array\u003CArray\u003Cunknown>>; \u002F\u002F Row data as arrays\n  lastInsertId?: string; \u002F\u002F For INSERT without RETURNING\n}\n```\n\n### ColumnTypeEnum values\n\n```typescript\nconst ColumnTypeEnum = {\n  Int32: 0,\n  Int64: 1,\n  Float: 2,\n  Double: 3,\n  Numeric: 4,\n  Boolean: 5,\n  Character: 6,\n  Text: 7,\n  Date: 8,\n  Time: 9,\n  DateTime: 10,\n  Json: 11,\n  Enum: 12,\n  Bytes: 13,\n  Set: 14,\n  Uuid: 15,\n  Int32Array: 64,\n  Int64Array: 65,\n  FloatArray: 66,\n  DoubleArray: 67,\n  NumericArray: 68,\n  BooleanArray: 69,\n  CharacterArray: 70,\n  TextArray: 71,\n  DateArray: 72,\n  TimeArray: 73,\n  DateTimeArray: 74,\n  JsonArray: 75,\n  EnumArray: 76,\n  BytesArray: 77,\n  UuidArray: 78,\n  UnknownNumber: 128,\n} as const;\n```\n\n### SqlDriverAdapter\n\n```typescript\ninterface SqlDriverAdapter extends SqlQueryable {\n  executeScript(script: string): Promise\u003Cvoid>;\n  startTransaction(isolationLevel?: IsolationLevel): Promise\u003CTransaction>;\n  getConnectionInfo?(): ConnectionInfo;\n  dispose(): Promise\u003Cvoid>;\n}\n```\n\n### Transaction\n\n```typescript\ninterface Transaction extends SqlQueryable {\n  readonly options: TransactionOptions;\n  commit(): Promise\u003Cvoid>;\n  rollback(): Promise\u003Cvoid>;\n}\n\ntype TransactionOptions = { usePhantomQuery: boolean };\n```\n\n### SqlMigrationAwareDriverAdapterFactory\n\n```typescript\ninterface SqlMigrationAwareDriverAdapterFactory {\n  readonly provider: \"mysql\" | \"postgres\" | \"sqlite\" | \"sqlserver\";\n  readonly adapterName: string;\n  connect(): Promise\u003CSqlDriverAdapter>;\n  connectToShadowDb(): Promise\u003CSqlDriverAdapter>;\n}\n```\n\n## Implementation Steps\n\n### Step 1: Create the Queryable base class\n\n```typescript\nclass MyQueryable\u003CTClient> implements SqlQueryable {\n  readonly provider = \"postgres\" as const; \u002F\u002F or 'sqlite' | 'mysql' | 'sqlserver'\n  readonly adapterName = \"@my-org\u002Fadapter-mydb\" as const;\n\n  constructor(protected readonly client: TClient) {}\n\n  async queryRaw(query: SqlQuery): Promise\u003CSqlResultSet> {\n    try {\n      const args = query.args.map((arg, i) =>\n        mapArg(arg, query.argTypes[i] ?? { scalarType: \"unknown\", arity: \"scalar\" })\n      );\n\n      \u002F\u002F Execute query with your driver\n      const result = await this.client.query(query.sql, args);\n\n      \u002F\u002F Extract column metadata\n      const columnNames = \u002F* get from result *\u002F;\n      const columnTypes = \u002F* map to ColumnTypeEnum *\u002F;\n\n      \u002F\u002F Map rows to ResultValue arrays\n      const rows = result.map(row => mapRow(row, columnTypes));\n\n      return { columnNames, columnTypes, rows };\n    } catch (e) {\n      this.onError(e);\n    }\n  }\n\n  async executeRaw(query: SqlQuery): Promise\u003Cnumber> {\n    try {\n      const args = query.args.map((arg, i) =>\n        mapArg(arg, query.argTypes[i] ?? { scalarType: \"unknown\", arity: \"scalar\" })\n      );\n      const result = await this.client.query(query.sql, args);\n      return result.affectedRows ?? 0;\n    } catch (e) {\n      this.onError(e);\n    }\n  }\n\n  protected onError(error: unknown): never {\n    throw new DriverAdapterError(convertDriverError(error));\n  }\n}\n```\n\n### Step 2: Create the Transaction class\n\n**Critical**: `commit()` and `rollback()` are **lifecycle hooks only**. They must NOT issue SQL. Prisma sends `COMMIT`\u002F`ROLLBACK` via `executeRaw` on the transaction object.\n\n```typescript\nclass MyTransaction extends MyQueryable\u003CTClient> implements Transaction {\n  readonly options: TransactionOptions;\n  readonly #release: () => void;\n\n  constructor(\n    client: TClient,\n    options: TransactionOptions,\n    release: () => void,\n  ) {\n    super(client);\n    this.options = options;\n    this.#release = release;\n  }\n\n  commit(): Promise\u003Cvoid> {\n    \u002F\u002F DO NOT issue COMMIT SQL here — Prisma does it via executeRaw\n    this.#release(); \u002F\u002F Release connection\u002Fresources\n    return Promise.resolve();\n  }\n\n  rollback(): Promise\u003Cvoid> {\n    \u002F\u002F DO NOT issue ROLLBACK SQL here — Prisma does it via executeRaw\n    this.#release();\n    return Promise.resolve();\n  }\n}\n```\n\n### Step 3: Create the Adapter class\n\n```typescript\nclass MyAdapter extends MyQueryable\u003CTClient> implements SqlDriverAdapter {\n  #transactionDepth = 0;\n\n  constructor(client: TClient) {\n    super(client);\n  }\n\n  async executeScript(script: string): Promise\u003Cvoid> {\n    \u002F\u002F For SQLite: split on ';' and run each statement\n    \u002F\u002F For Postgres: use multi-statement execution\n    try {\n      \u002F\u002F Implementation depends on driver capabilities\n    } catch (e) {\n      this.onError(e);\n    }\n  }\n\n  async startTransaction(\n    isolationLevel?: IsolationLevel,\n  ): Promise\u003CTransaction> {\n    \u002F\u002F Validate isolation level for your database\n    const validLevels = new Set\u003CIsolationLevel>([\n      \"READ UNCOMMITTED\",\n      \"READ COMMITTED\",\n      \"REPEATABLE READ\",\n      \"SERIALIZABLE\",\n    ]);\n\n    if (isolationLevel !== undefined && !validLevels.has(isolationLevel)) {\n      throw new DriverAdapterError({\n        kind: \"InvalidIsolationLevel\",\n        level: isolationLevel,\n      });\n    }\n\n    const options: TransactionOptions = { usePhantomQuery: false };\n\n    this.#transactionDepth += 1;\n    const depth = this.#transactionDepth;\n\n    try {\n      if (depth === 1) {\n        \u002F\u002F Issue BEGIN (with isolation level if specified)\n        const beginSql = isolationLevel\n          ? `BEGIN ISOLATION LEVEL ${isolationLevel}`\n          : \"BEGIN\";\n        await this.client.query(beginSql);\n      } else {\n        \u002F\u002F Nested: use savepoints\n        await this.client.query(`SAVEPOINT sp_${depth}`);\n      }\n    } catch (e) {\n      this.#transactionDepth -= 1;\n      this.onError(e);\n    }\n\n    const release = () => {\n      this.#transactionDepth -= 1;\n    };\n    return new MyTransaction(this.client, options, release);\n  }\n\n  getConnectionInfo(): ConnectionInfo {\n    return { supportsRelationJoins: true };\n  }\n\n  async dispose(): Promise\u003Cvoid> {\n    await this.client.close();\n  }\n}\n```\n\n### Step 4: Create the Factory class\n\n```typescript\nexport type MyAdapterConfig = {\n  url: string;\n};\n\nexport type MyAdapterOptions = {\n  shadowDatabaseUrl?: string;\n};\n\nexport class MyAdapterFactory implements SqlMigrationAwareDriverAdapterFactory {\n  readonly provider = \"postgres\" as const;\n  readonly adapterName = \"@my-org\u002Fadapter-mydb\" as const;\n\n  constructor(\n    private readonly config: MyAdapterConfig,\n    private readonly options?: MyAdapterOptions,\n  ) {}\n\n  connect(): Promise\u003CSqlDriverAdapter> {\n    return Promise.resolve(new MyAdapter(openConnection(this.config.url)));\n  }\n\n  connectToShadowDb(): Promise\u003CSqlDriverAdapter> {\n    const url = this.options?.shadowDatabaseUrl ?? this.config.url;\n    return Promise.resolve(new MyAdapter(openConnection(url)));\n  }\n}\n```\n\n## Conversion Helpers\n\n### Argument Mapping (input)\n\nConvert Prisma argument values to driver-native types:\n\n```typescript\nfunction mapArg(arg: unknown, argType: ArgType): unknown {\n  if (arg === null || arg === undefined) return null;\n\n  \u002F\u002F String → number for int columns\n  if (typeof arg === \"string\" && argType.scalarType === \"int\")\n    return Number.parseInt(arg, 10);\n\n  \u002F\u002F String → number for float columns\n  if (typeof arg === \"string\" && argType.scalarType === \"float\")\n    return Number.parseFloat(arg);\n\n  \u002F\u002F String → BigInt for bigint columns\n  if (typeof arg === \"string\" && argType.scalarType === \"bigint\")\n    return BigInt(arg);\n\n  \u002F\u002F Base64 string → Buffer for bytes columns\n  if (typeof arg === \"string\" && argType.scalarType === \"bytes\")\n    return Buffer.from(arg, \"base64\");\n\n  \u002F\u002F Boolean → 0\u002F1 for SQLite\n  if (typeof arg === \"boolean\" && \u002F* SQLite *\u002F)\n    return arg ? 1 : 0;\n\n  return arg;\n}\n```\n\n### Row Mapping (output)\n\nConvert driver result values to Prisma-expected types:\n\n```typescript\nfunction mapRow(row: unknown[], columnTypes: ColumnType[]): ResultValue[] {\n  const result: ResultValue[] = [];\n\n  for (let i = 0; i \u003C row.length; i++) {\n    const value = row[i] ?? null;\n    const colType = columnTypes[i];\n\n    if (value === null) {\n      result.push(null);\n      continue;\n    }\n\n    \u002F\u002F bigint → string for Int64 (JSON-safe)\n    if (typeof value === \"bigint\") {\n      result.push(value.toString());\n      continue;\n    }\n\n    \u002F\u002F Date → ISO 8601 string for DateTime\n    if (value instanceof Date) {\n      result.push(value.toISOString());\n      continue;\n    }\n\n    \u002F\u002F JSON objects → stringified\n    if (colType === ColumnTypeEnum.Json && typeof value === \"object\") {\n      result.push(JSON.stringify(value));\n      continue;\n    }\n\n    result.push(value as ResultValue);\n  }\n\n  return result;\n}\n```\n\n### Column Type Inference\n\nWhen the driver doesn't provide type metadata, infer from JS values:\n\n```typescript\nfunction inferColumnType(value: NonNullable\u003Cunknown>): ColumnType {\n  if (typeof value === \"boolean\") return ColumnTypeEnum.Boolean;\n  if (typeof value === \"bigint\") return ColumnTypeEnum.Int64;\n  if (value instanceof Uint8Array) return ColumnTypeEnum.Bytes;\n  if (value instanceof Date) return ColumnTypeEnum.DateTime;\n  if (Array.isArray(value)) return ColumnTypeEnum.Text; \u002F\u002F fallback\n  if (typeof value === \"object\") return ColumnTypeEnum.Json;\n  if (typeof value === \"number\") return ColumnTypeEnum.UnknownNumber;\n  return ColumnTypeEnum.Text;\n}\n```\n\n## Error Handling\n\nMap driver errors to `MappedError` for Prisma to handle correctly:\n\n```typescript\nfunction convertDriverError(error: unknown): MappedError {\n  if (error instanceof Error) {\n    \u002F\u002F Database-specific error mapping\n    const dbError = error as Error & { code?: string; errno?: number };\n\n    \u002F\u002F PostgreSQL example\n    if (dbError.code === \"23505\") {\n      return { kind: \"UniqueConstraintViolation\" };\n    }\n    if (dbError.code === \"23502\") {\n      return { kind: \"NullConstraintViolation\" };\n    }\n    if (dbError.code === \"23503\") {\n      return { kind: \"ForeignKeyConstraintViolation\" };\n    }\n    if (dbError.code === \"42P01\") {\n      return { kind: \"TableDoesNotExist\" };\n    }\n\n    \u002F\u002F SQLite example\n    if (error.name === \"SQLiteError\") {\n      return {\n        kind: \"sqlite\",\n        extendedCode: dbError.errno ?? 1,\n        message: error.message,\n      };\n    }\n\n    \u002F\u002F PostgreSQL raw error\n    if (dbError.code) {\n      return {\n        kind: \"postgres\",\n        code: dbError.code,\n        severity: \"ERROR\",\n        message: error.message,\n        detail: undefined,\n        column: undefined,\n        hint: undefined,\n      };\n    }\n  }\n\n  return { kind: \"GenericJs\", id: 0 };\n}\n```\n\n## Database-Specific Notes\n\n### SQLite\n\n- Set `safeIntegers: true` when opening the database to get `bigint` for large integers\n- Only `SERIALIZABLE` isolation level is valid\n- `executeScript`: split on `;` and run each statement individually\n- Boolean values: store as 0\u002F1, return as boolean\n\n### PostgreSQL\n\n- All standard isolation levels are valid\n- For connection pooling (PgBouncer), use `prepare: false`\n- Transactions require a dedicated connection (`reserve()` pattern)\n- `executeScript`: use multi-statement execution (`.simple()` in some drivers)\n- `int8` columns may return as string (already stringified by driver)\n- `numeric` columns return as string to preserve precision\n\n### MySQL\u002FMariaDB\n\n- Supports `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, `SERIALIZABLE`\n- Use `?` placeholders for parameters\n- Handle `BIGINT` as string for large values\n\n## Testing Strategy\n\n### Unit Tests (no PrismaClient)\n\nTest the adapter directly with the raw database driver:\n\n```typescript\ndescribe(\"queryRaw\", () => {\n  test(\"returns column names and types\", async () => {\n    const adapter = new MyAdapter(createTestConnection());\n    const result = await adapter.queryRaw({\n      sql: \"SELECT id, name FROM users\",\n      args: [],\n      argTypes: [],\n    });\n    expect(result.columnNames).toEqual([\"id\", \"name\"]);\n    expect(result.columnTypes[0]).toBe(ColumnTypeEnum.Int32);\n  });\n});\n\ndescribe(\"startTransaction\", () => {\n  test(\"commit persists changes\", async () => {\n    const adapter = new MyAdapter(createTestConnection());\n    const tx = await adapter.startTransaction();\n    await tx.executeRaw({\n      sql: \"INSERT INTO users (name) VALUES (?)\",\n      args: [\"Alice\"],\n      argTypes: [],\n    });\n    \u002F\u002F Prisma sends COMMIT via executeRaw\n    await tx.executeRaw({ sql: \"COMMIT\", args: [], argTypes: [] });\n    await tx.commit(); \u002F\u002F lifecycle hook only\n    \u002F\u002F Verify data persisted\n  });\n});\n```\n\n### E2E Tests (with PrismaClient)\n\nTest the full integration:\n\n```typescript\ndescribe(\"E2E\", () => {\n  let prisma: PrismaClient;\n\n  beforeEach(async () => {\n    const factory = new MyAdapterFactory({ url: TEST_DB_URL });\n    prisma = new PrismaClient({ adapter: factory });\n  });\n\n  test(\"CRUD operations\", async () => {\n    const user = await prisma.user.create({ data: { name: \"Alice\" } });\n    expect(user.id).toBeGreaterThan(0);\n\n    const found = await prisma.user.findUnique({ where: { id: user.id } });\n    expect(found?.name).toBe(\"Alice\");\n  });\n\n  test(\"transactions roll back on error\", async () => {\n    await expect(\n      prisma.$transaction(async (tx) => {\n        await tx.user.create({ data: { name: \"Bob\" } });\n        throw new Error(\"Rollback!\");\n      }),\n    ).rejects.toThrow();\n\n    expect(await prisma.user.count()).toBe(0);\n  });\n});\n```\n\n## Usage Example\n\n```typescript\nimport { PrismaClient } from \".\u002Fgenerated\u002Fprisma\u002Fclient\";\nimport { MyAdapterFactory } from \"@my-org\u002Fadapter-mydb\";\n\nconst factory = new MyAdapterFactory({\n  url: process.env.DATABASE_URL!,\n});\n\nconst prisma = new PrismaClient({ adapter: factory });\n\n\u002F\u002F Use prisma normally\nconst users = await prisma.user.findMany();\n```\n\n## Checklist\n\nBefore considering the adapter complete:\n\n- [ ] `SqlMigrationAwareDriverAdapterFactory` implemented with `connect()` and `connectToShadowDb()`\n- [ ] `SqlDriverAdapter` implements `queryRaw`, `executeRaw`, `executeScript`, `startTransaction`, `dispose`\n- [ ] `Transaction` implements `queryRaw`, `executeRaw`, `commit`, `rollback` with `options: { usePhantomQuery: false }`\n- [ ] `commit()` and `rollback()` are lifecycle hooks only (no SQL issued)\n- [ ] `startTransaction` issues `BEGIN` (depth 1) or `SAVEPOINT sp_N` (nested)\n- [ ] Argument mapping handles: string→int, string→bigint, string→float, base64→bytes\n- [ ] Row mapping handles: bigint→string, Date→ISO string, JSON→string\n- [ ] Column types correctly mapped to `ColumnTypeEnum`\n- [ ] Errors wrapped in `DriverAdapterError` with proper `MappedError` kind\n- [ ] Isolation level validation for the target database\n- [ ] Unit tests pass for queryRaw, executeRaw, executeScript, transactions\n- [ ] E2E tests pass with real PrismaClient\n","# Prisma 7 Driver Adapter Implementation Guide\n\nThis skill provides everything needed to implement a Prisma ORM v7 driver adapter for any database.\n\n## Architecture Overview\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                         PrismaClient                            │\n│                    (requires adapter factory)                   │\n└─────────────────────────────────────────────────────────────────┘\n                                │\n                                ▼\n┌─────────────────────────────────────────────────────────────────┐\n│            SqlMigrationAwareDriverAdapterFactory                │\n│   ┌─────────────────────┐    ┌─────────────────────────────┐    │\n│   │ connect()           │    │ connectToShadowDb()         │    │\n│   │ → SqlDriverAdapter  │    │ → SqlDriverAdapter          │    │\n│   └─────────────────────┘    └─────────────────────────────┘    │\n└─────────────────────────────────────────────────────────────────┘\n                                │\n                                ▼\n┌─────────────────────────────────────────────────────────────────┐\n│                      SqlDriverAdapter                           │\n│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │\n│  │ queryRaw()   │ │ executeRaw() │ │ startTransaction()       │ │\n│  │ → ResultSet  │ │ → number     │ │ → Transaction            │ │\n│  └──────────────┘ └──────────────┘ └──────────────────────────┘ │\n│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │\n│  │executeScript │ │ dispose()    │ │ getConnectionInfo()      │ │\n│  └──────────────┘ └──────────────┘ └──────────────────────────┘ │\n└─────────────────────────────────────────────────────────────────┘\n                                │\n                                ▼\n┌─────────────────────────────────────────────────────────────────┐\n│                        Transaction                              │\n│  Extends SqlQueryable + commit() + rollback() + options         │\n│  (lifecycle hooks only — Prisma sends SQL via executeRaw)       │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Required Interfaces\n\nImport from `@prisma\u002Fdriver-adapter-utils`:\n\n```typescript\nimport type {\n  ColumnType,\n  IsolationLevel,\n  SqlDriverAdapter,\n  SqlMigrationAwareDriverAdapterFactory,\n  SqlQuery,\n  SqlQueryable,\n  SqlResultSet,\n  Transaction,\n  TransactionOptions,\n  ArgType,\n  ConnectionInfo,\n  MappedError,\n} from \"@prisma\u002Fdriver-adapter-utils\";\nimport {\n  ColumnTypeEnum,\n  DriverAdapterError,\n} from \"@prisma\u002Fdriver-adapter-utils\";\n```\n\n## Interface Definitions\n\n### SqlQuery (input to queryRaw\u002FexecuteRaw)\n\n```typescript\ntype SqlQuery = {\n  sql: string; \u002F\u002F Parameterized SQL with placeholders\n  args: Array\u003Cunknown>; \u002F\u002F Bound parameter values\n  argTypes: Array\u003CArgType>; \u002F\u002F Type hints for each argument\n};\n\ntype ArgType = {\n  scalarType: ArgScalarType; \u002F\u002F 'string' | 'int' | 'bigint' | 'float' | 'decimal' | 'boolean' | 'enum' | 'uuid' | 'json' | 'datetime' | 'bytes' | 'unknown'\n  dbType?: string;\n  arity: \"scalar\" | \"list\";\n};\n```\n\n### SqlResultSet (output from queryRaw)\n\n```typescript\ninterface SqlResultSet {\n  columnNames: Array\u003Cstring>; \u002F\u002F Column names in order\n  columnTypes: Array\u003CColumnType>; \u002F\u002F Column types matching columnNames\n  rows: Array\u003CArray\u003Cunknown>>; \u002F\u002F Row data as arrays\n  lastInsertId?: string; \u002F\u002F For INSERT without RETURNING\n}\n```\n\n### ColumnTypeEnum values\n\n```typescript\nconst ColumnTypeEnum = {\n  Int32: 0,\n  Int64: 1,\n  Float: 2,\n  Double: 3,\n  Numeric: 4,\n  Boolean: 5,\n  Character: 6,\n  Text: 7,\n  Date: 8,\n  Time: 9,\n  DateTime: 10,\n  Json: 11,\n  Enum: 12,\n  Bytes: 13,\n  Set: 14,\n  Uuid: 15,\n  Int32Array: 64,\n  Int64Array: 65,\n  FloatArray: 66,\n  DoubleArray: 67,\n  NumericArray: 68,\n  BooleanArray: 69,\n  CharacterArray: 70,\n  TextArray: 71,\n  DateArray: 72,\n  TimeArray: 73,\n  DateTimeArray: 74,\n  JsonArray: 75,\n  EnumArray: 76,\n  BytesArray: 77,\n  UuidArray: 78,\n  UnknownNumber: 128,\n} as const;\n```\n\n### SqlDriverAdapter\n\n```typescript\ninterface SqlDriverAdapter extends SqlQueryable {\n  executeScript(script: string): Promise\u003Cvoid>;\n  startTransaction(isolationLevel?: IsolationLevel): Promise\u003CTransaction>;\n  getConnectionInfo?(): ConnectionInfo;\n  dispose(): Promise\u003Cvoid>;\n}\n```\n\n### Transaction\n\n```typescript\ninterface Transaction extends SqlQueryable {\n  readonly options: TransactionOptions;\n  commit(): Promise\u003Cvoid>;\n  rollback(): Promise\u003Cvoid>;\n}\n\ntype TransactionOptions = { usePhantomQuery: boolean };\n```\n\n### SqlMigrationAwareDriverAdapterFactory\n\n```typescript\ninterface SqlMigrationAwareDriverAdapterFactory {\n  readonly provider: \"mysql\" | \"postgres\" | \"sqlite\" | \"sqlserver\";\n  readonly adapterName: string;\n  connect(): Promise\u003CSqlDriverAdapter>;\n  connectToShadowDb(): Promise\u003CSqlDriverAdapter>;\n}\n```\n\n## Implementation Steps\n\n### Step 1: Create the Queryable base class\n\n```typescript\nclass MyQueryable\u003CTClient> implements SqlQueryable {\n  readonly provider = \"postgres\" as const; \u002F\u002F or 'sqlite' | 'mysql' | 'sqlserver'\n  readonly adapterName = \"@my-org\u002Fadapter-mydb\" as const;\n\n  constructor(protected readonly client: TClient) {}\n\n  async queryRaw(query: SqlQuery): Promise\u003CSqlResultSet> {\n    try {\n      const args = query.args.map((arg, i) =>\n        mapArg(arg, query.argTypes[i] ?? { scalarType: \"unknown\", arity: \"scalar\" })\n      );\n\n      \u002F\u002F Execute query with your driver\n      const result = await this.client.query(query.sql, args);\n\n      \u002F\u002F Extract column metadata\n      const columnNames = \u002F* get from result *\u002F;\n      const columnTypes = \u002F* map to ColumnTypeEnum *\u002F;\n\n      \u002F\u002F Map rows to ResultValue arrays\n      const rows = result.map(row => mapRow(row, columnTypes));\n\n      return { columnNames, columnTypes, rows };\n    } catch (e) {\n      this.onError(e);\n    }\n  }\n\n  async executeRaw(query: SqlQuery): Promise\u003Cnumber> {\n    try {\n      const args = query.args.map((arg, i) =>\n        mapArg(arg, query.argTypes[i] ?? { scalarType: \"unknown\", arity: \"scalar\" })\n      );\n      const result = await this.client.query(query.sql, args);\n      return result.affectedRows ?? 0;\n    } catch (e) {\n      this.onError(e);\n    }\n  }\n\n  protected onError(error: unknown): never {\n    throw new DriverAdapterError(convertDriverError(error));\n  }\n}\n```\n\n### Step 2: Create the Transaction class\n\n**Critical**: `commit()` and `rollback()` are **lifecycle hooks only**. They must NOT issue SQL. Prisma sends `COMMIT`\u002F`ROLLBACK` via `executeRaw` on the transaction object.\n\n```typescript\nclass MyTransaction extends MyQueryable\u003CTClient> implements Transaction {\n  readonly options: TransactionOptions;\n  readonly #release: () => void;\n\n  constructor(\n    client: TClient,\n    options: TransactionOptions,\n    release: () => void,\n  ) {\n    super(client);\n    this.options = options;\n    this.#release = release;\n  }\n\n  commit(): Promise\u003Cvoid> {\n    \u002F\u002F DO NOT issue COMMIT SQL here — Prisma does it via executeRaw\n    this.#release(); \u002F\u002F Release connection\u002Fresources\n    return Promise.resolve();\n  }\n\n  rollback(): Promise\u003Cvoid> {\n    \u002F\u002F DO NOT issue ROLLBACK SQL here — Prisma does it via executeRaw\n    this.#release();\n    return Promise.resolve();\n  }\n}\n```\n\n### Step 3: Create the Adapter class\n\n```typescript\nclass MyAdapter extends MyQueryable\u003CTClient> implements SqlDriverAdapter {\n  #transactionDepth = 0;\n\n  constructor(client: TClient) {\n    super(client);\n  }\n\n  async executeScript(script: string): Promise\u003Cvoid> {\n    \u002F\u002F For SQLite: split on ';' and run each statement\n    \u002F\u002F For Postgres: use multi-statement execution\n    try {\n      \u002F\u002F Implementation depends on driver capabilities\n    } catch (e) {\n      this.onError(e);\n    }\n  }\n\n  async startTransaction(\n    isolationLevel?: IsolationLevel,\n  ): Promise\u003CTransaction> {\n    \u002F\u002F Validate isolation level for your database\n    const validLevels = new Set\u003CIsolationLevel>([\n      \"READ UNCOMMITTED\",\n      \"READ COMMITTED\",\n      \"REPEATABLE READ\",\n      \"SERIALIZABLE\",\n    ]);\n\n    if (isolationLevel !== undefined && !validLevels.has(isolationLevel)) {\n      throw new DriverAdapterError({\n        kind: \"InvalidIsolationLevel\",\n        level: isolationLevel,\n      });\n    }\n\n    const options: TransactionOptions = { usePhantomQuery: false };\n\n    this.#transactionDepth += 1;\n    const depth = this.#transactionDepth;\n\n    try {\n      if (depth === 1) {\n        \u002F\u002F Issue BEGIN (with isolation level if specified)\n        const beginSql = isolationLevel\n          ? `BEGIN ISOLATION LEVEL ${isolationLevel}`\n          : \"BEGIN\";\n        await this.client.query(beginSql);\n      } else {\n        \u002F\u002F Nested: use savepoints\n        await this.client.query(`SAVEPOINT sp_${depth}`);\n      }\n    } catch (e) {\n      this.#transactionDepth -= 1;\n      this.onError(e);\n    }\n\n    const release = () => {\n      this.#transactionDepth -= 1;\n    };\n    return new MyTransaction(this.client, options, release);\n  }\n\n  getConnectionInfo(): ConnectionInfo {\n    return { supportsRelationJoins: true };\n  }\n\n  async dispose(): Promise\u003Cvoid> {\n    await this.client.close();\n  }\n}\n```\n\n### Step 4: Create the Factory class\n\n```typescript\nexport type MyAdapterConfig = {\n  url: string;\n};\n\nexport type MyAdapterOptions = {\n  shadowDatabaseUrl?: string;\n};\n\nexport class MyAdapterFactory implements SqlMigrationAwareDriverAdapterFactory {\n  readonly provider = \"postgres\" as const;\n  readonly adapterName = \"@my-org\u002Fadapter-mydb\" as const;\n\n  constructor(\n    private readonly config: MyAdapterConfig,\n    private readonly options?: MyAdapterOptions,\n  ) {}\n\n  connect(): Promise\u003CSqlDriverAdapter> {\n    return Promise.resolve(new MyAdapter(openConnection(this.config.url)));\n  }\n\n  connectToShadowDb(): Promise\u003CSqlDriverAdapter> {\n    const url = this.options?.shadowDatabaseUrl ?? this.config.url;\n    return Promise.resolve(new MyAdapter(openConnection(url)));\n  }\n}\n```\n\n## Conversion Helpers\n\n### Argument Mapping (input)\n\nConvert Prisma argument values to driver-native types:\n\n```typescript\nfunction mapArg(arg: unknown, argType: ArgType): unknown {\n  if (arg === null || arg === undefined) return null;\n\n  \u002F\u002F String → number for int columns\n  if (typeof arg === \"string\" && argType.scalarType === \"int\")\n    return Number.parseInt(arg, 10);\n\n  \u002F\u002F String → number for float columns\n  if (typeof arg === \"string\" && argType.scalarType === \"float\")\n    return Number.parseFloat(arg);\n\n  \u002F\u002F String → BigInt for bigint columns\n  if (typeof arg === \"string\" && argType.scalarType === \"bigint\")\n    return BigInt(arg);\n\n  \u002F\u002F Base64 string → Buffer for bytes columns\n  if (typeof arg === \"string\" && argType.scalarType === \"bytes\")\n    return Buffer.from(arg, \"base64\");\n\n  \u002F\u002F Boolean → 0\u002F1 for SQLite\n  if (typeof arg === \"boolean\" && \u002F* SQLite *\u002F)\n    return arg ? 1 : 0;\n\n  return arg;\n}\n```\n\n### Row Mapping (output)\n\nConvert driver result values to Prisma-expected types:\n\n```typescript\nfunction mapRow(row: unknown[], columnTypes: ColumnType[]): ResultValue[] {\n  const result: ResultValue[] = [];\n\n  for (let i = 0; i \u003C row.length; i++) {\n    const value = row[i] ?? null;\n    const colType = columnTypes[i];\n\n    if (value === null) {\n      result.push(null);\n      continue;\n    }\n\n    \u002F\u002F bigint → string for Int64 (JSON-safe)\n    if (typeof value === \"bigint\") {\n      result.push(value.toString());\n      continue;\n    }\n\n    \u002F\u002F Date → ISO 8601 string for DateTime\n    if (value instanceof Date) {\n      result.push(value.toISOString());\n      continue;\n    }\n\n    \u002F\u002F JSON objects → stringified\n    if (colType === ColumnTypeEnum.Json && typeof value === \"object\") {\n      result.push(JSON.stringify(value));\n      continue;\n    }\n\n    result.push(value as ResultValue);\n  }\n\n  return result;\n}\n```\n\n### Column Type Inference\n\nWhen the driver doesn't provide type metadata, infer from JS values:\n\n```typescript\nfunction inferColumnType(value: NonNullable\u003Cunknown>): ColumnType {\n  if (typeof value === \"boolean\") return ColumnTypeEnum.Boolean;\n  if (typeof value === \"bigint\") return ColumnTypeEnum.Int64;\n  if (value instanceof Uint8Array) return ColumnTypeEnum.Bytes;\n  if (value instanceof Date) return ColumnTypeEnum.DateTime;\n  if (Array.isArray(value)) return ColumnTypeEnum.Text; \u002F\u002F fallback\n  if (typeof value === \"object\") return ColumnTypeEnum.Json;\n  if (typeof value === \"number\") return ColumnTypeEnum.UnknownNumber;\n  return ColumnTypeEnum.Text;\n}\n```\n\n## Error Handling\n\nMap driver errors to `MappedError` for Prisma to handle correctly:\n\n```typescript\nfunction convertDriverError(error: unknown): MappedError {\n  if (error instanceof Error) {\n    \u002F\u002F Database-specific error mapping\n    const dbError = error as Error & { code?: string; errno?: number };\n\n    \u002F\u002F PostgreSQL example\n    if (dbError.code === \"23505\") {\n      return { kind: \"UniqueConstraintViolation\" };\n    }\n    if (dbError.code === \"23502\") {\n      return { kind: \"NullConstraintViolation\" };\n    }\n    if (dbError.code === \"23503\") {\n      return { kind: \"ForeignKeyConstraintViolation\" };\n    }\n    if (dbError.code === \"42P01\") {\n      return { kind: \"TableDoesNotExist\" };\n    }\n\n    \u002F\u002F SQLite example\n    if (error.name === \"SQLiteError\") {\n      return {\n        kind: \"sqlite\",\n        extendedCode: dbError.errno ?? 1,\n        message: error.message,\n      };\n    }\n\n    \u002F\u002F PostgreSQL raw error\n    if (dbError.code) {\n      return {\n        kind: \"postgres\",\n        code: dbError.code,\n        severity: \"ERROR\",\n        message: error.message,\n        detail: undefined,\n        column: undefined,\n        hint: undefined,\n      };\n    }\n  }\n\n  return { kind: \"GenericJs\", id: 0 };\n}\n```\n\n## Database-Specific Notes\n\n### SQLite\n\n- Set `safeIntegers: true` when opening the database to get `bigint` for large integers\n- Only `SERIALIZABLE` isolation level is valid\n- `executeScript`: split on `;` and run each statement individually\n- Boolean values: store as 0\u002F1, return as boolean\n\n### PostgreSQL\n\n- All standard isolation levels are valid\n- For connection pooling (PgBouncer), use `prepare: false`\n- Transactions require a dedicated connection (`reserve()` pattern)\n- `executeScript`: use multi-statement execution (`.simple()` in some drivers)\n- `int8` columns may return as string (already stringified by driver)\n- `numeric` columns return as string to preserve precision\n\n### MySQL\u002FMariaDB\n\n- Supports `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, `SERIALIZABLE`\n- Use `?` placeholders for parameters\n- Handle `BIGINT` as string for large values\n\n## Testing Strategy\n\n### Unit Tests (no PrismaClient)\n\nTest the adapter directly with the raw database driver:\n\n```typescript\ndescribe(\"queryRaw\", () => {\n  test(\"returns column names and types\", async () => {\n    const adapter = new MyAdapter(createTestConnection());\n    const result = await adapter.queryRaw({\n      sql: \"SELECT id, name FROM users\",\n      args: [],\n      argTypes: [],\n    });\n    expect(result.columnNames).toEqual([\"id\", \"name\"]);\n    expect(result.columnTypes[0]).toBe(ColumnTypeEnum.Int32);\n  });\n});\n\ndescribe(\"startTransaction\", () => {\n  test(\"commit persists changes\", async () => {\n    const adapter = new MyAdapter(createTestConnection());\n    const tx = await adapter.startTransaction();\n    await tx.executeRaw({\n      sql: \"INSERT INTO users (name) VALUES (?)\",\n      args: [\"Alice\"],\n      argTypes: [],\n    });\n    \u002F\u002F Prisma sends COMMIT via executeRaw\n    await tx.executeRaw({ sql: \"COMMIT\", args: [], argTypes: [] });\n    await tx.commit(); \u002F\u002F lifecycle hook only\n    \u002F\u002F Verify data persisted\n  });\n});\n```\n\n### E2E Tests (with PrismaClient)\n\nTest the full integration:\n\n```typescript\ndescribe(\"E2E\", () => {\n  let prisma: PrismaClient;\n\n  beforeEach(async () => {\n    const factory = new MyAdapterFactory({ url: TEST_DB_URL });\n    prisma = new PrismaClient({ adapter: factory });\n  });\n\n  test(\"CRUD operations\", async () => {\n    const user = await prisma.user.create({ data: { name: \"Alice\" } });\n    expect(user.id).toBeGreaterThan(0);\n\n    const found = await prisma.user.findUnique({ where: { id: user.id } });\n    expect(found?.name).toBe(\"Alice\");\n  });\n\n  test(\"transactions roll back on error\", async () => {\n    await expect(\n      prisma.$transaction(async (tx) => {\n        await tx.user.create({ data: { name: \"Bob\" } });\n        throw new Error(\"Rollback!\");\n      }),\n    ).rejects.toThrow();\n\n    expect(await prisma.user.count()).toBe(0);\n  });\n});\n```\n\n## Usage Example\n\n```typescript\nimport { PrismaClient } from \".\u002Fgenerated\u002Fprisma\u002Fclient\";\nimport { MyAdapterFactory } from \"@my-org\u002Fadapter-mydb\";\n\nconst factory = new MyAdapterFactory({\n  url: process.env.DATABASE_URL!,\n});\n\nconst prisma = new PrismaClient({ adapter: factory });\n\n\u002F\u002F Use prisma normally\nconst users = await prisma.user.findMany();\n```\n\n## Checklist\n\nBefore considering the adapter complete:\n\n- [ ] `SqlMigrationAwareDriverAdapterFactory` implemented with `connect()` and `connectToShadowDb()`\n- [ ] `SqlDriverAdapter` implements `queryRaw`, `executeRaw`, `executeScript`, `startTransaction`, `dispose`\n- [ ] `Transaction` implements `queryRaw`, `executeRaw`, `commit`, `rollback` with `options: { usePhantomQuery: false }`\n- [ ] `commit()` and `rollback()` are lifecycle hooks only (no SQL issued)\n- [ ] `startTransaction` issues `BEGIN` (depth 1) or `SAVEPOINT sp_N` (nested)\n- [ ] Argument mapping handles: string→int, string→bigint, string→float, base64→bytes\n- [ ] Row mapping handles: bigint→string, Date→ISO string, JSON→string\n- [ ] Column types correctly mapped to `ColumnTypeEnum`\n- [ ] Errors wrapped in `DriverAdapterError` with proper `MappedError` kind\n- [ ] Isolation level validation for the target database\n- [ ] Unit tests pass for queryRaw, executeRaw, executeScript, transactions\n- [ ] E2E tests pass with real PrismaClient\n",{"name":5,"license":15,"metadata":49,"description":7},{"author":50,"version":51},"Tyler Benfield","7.6.0","master","c2e410e11df4d80d885e0ecf0583ef3c926df736","factory\u002Flibrary\u002Fskills\u002Fofficial_prisma_prisma_driver_adapter_implementation",0,"https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory","https:\u002F\u002Fgithub.com\u002FiDorgham","Dorgham.me","hey I'm Dorgham \r\nI code, I create, and AI codes back. \r\nMaybe we can give AI the chance!! now we begin. 🚀🤖\r\n... . . . git commit -m \"Hello, Future\" ","2026-05-04 18:39:09",{"source":8,"skillName":5,"commands":62,"prompt":72,"zipAvailable":73},[63,66,69],{"tool":64,"command":65},"npx","npx skills add iDorgham\u002FWorkspace-Factory --skill prisma-driver-adapter-implementation",{"tool":67,"command":68},"bunx","bunx skills add iDorgham\u002FWorkspace-Factory --skill prisma-driver-adapter-implementation",{"tool":70,"command":71},"pnpm","pnpm dlx skills add iDorgham\u002FWorkspace-Factory --skill prisma-driver-adapter-implementation","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_prisma_prisma_driver_adapter_implementation\u002Fskill.md",true,[],[76,105,127,148,167,186],{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":85,"publishedAt":104},"01M71P8WVBMB2X3D24R0YT0B0E","vercel-react-view-transitions","Guide for implementing smooth, native-feeling animations using React's View Transition API (`\u003CViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter\u002Fexit of components, animate list reorder, implement directional (forward\u002Fback) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view transitions, `startViewTransition`, `ViewTransition`, transition types, or asks about animating between UI states in React without third-party animation libraries.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_react_view_transitions\u002Fskill.md",[82],{"categoryNo":83,"categoryName":84,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[86,87,88,89,90,91,92,93,94,95,96,97,98,42,39,99,100,101,102,103],"react","vercel react view transitions","view","react view transitions","vercel","transitions","shared","back","Animation","css","types","states","element","components","api","route","create","libraries","2026-11-10 13:02:02",{"skillNo":106,"name":107,"nameZh":6,"description":108,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":112,"publishedAt":104},"01M71P8TDGHZ289TSAC5VTG2EW","vercel-react-native-skills","React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_react_native_skills\u002Fskill.md",[111],{"categoryNo":83,"categoryName":84,"taxonomyType":21,"taxonomyTypeDesc":22},[86,113,114,90,115,116,117,118,42,119,39,120,99,121,122,123,124,125,126,33],"react native","native","vercel react native skills","react native skills","modules","performance","apps","tasks","mobile","expo","list","mobile performance","platform","animations",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":134,"publishedAt":104},"01M71P8QK4JF5BYRGYDDGPZQG9","vercel-react-best-practices","React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React\u002FNext.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_react_best_practices\u002Fskill.md",[133],{"categoryNo":83,"categoryName":84,"taxonomyType":21,"taxonomyTypeDesc":22},[86,135,136,90,137,118,138,39,38,120,99,139,140,141,142,143,144,145,146,147],"react best practices","vercel react best practices","best practices","writing","data fetching","react components","next","refactoring","data","patterns","Optimization","guidelines","triggers",{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":155,"publishedAt":104},"01M71P8HVEA70K5N6ZVJCG2ED3","vercel-composition-patterns","React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_composition_patterns\u002Fskill.md",[154],{"categoryNo":83,"categoryName":84,"taxonomyType":21,"taxonomyTypeDesc":22},[156,90,157,144,158,159,160,161,147,162,163,30,39,164,120,99,165,166,100,86],"Composition","composition patterns","vercel composition patterns","react composition patterns","react 19","render","boolean","component architecture","reusable","architecture","react composition",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":174,"publishedAt":104},"01M71P60XXKKP222ZFJRG7R373","supabase-postgres-best-practices","Postgres performance optimization and best practices from Supabase. Use this skill when writing, reviewing, or optimizing Postgres queries, schema designs, or database configurations.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_supabase_supabase_postgres_best_practices\u002Fskill.md",[173],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[175,176,177,178,137,179,180,181,33,145,118,182,183,138,184,30,185,41,40,39],"postgres best practices","supabase","postgres","supabase postgres best practices","categories","postgres performance","advanced","correct","schema","impact","Rules",{"skillNo":187,"name":188,"nameZh":6,"description":189,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":190,"categories":191,"keywords":193,"publishedAt":44},"01M71P2ZCEJD0PKC458YTP1FJW","portable-text-serialization","Render and serialize Portable Text to React, Svelte, Vue, Astro, HTML, Markdown, and plain text. Use when implementing Portable Text rendering in any frontend framework, building custom serializers for non-standard block types, converting Portable Text to HTML strings server-side, converting Portable Text to Markdown, extracting plain text from Portable Text, or troubleshooting rendering issues with marks, blocks, lists, or custom types.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_sanity_io_portable_text_serialization\u002Fskill.md",[192],{"categoryNo":83,"categoryName":84,"taxonomyType":21,"taxonomyTypeDesc":22},[194,195,196,197,198,96,199,161,200,42,201,202,39,86,203,204,205,206,207,208],"text","portable text serialization","portable","serialization","extracting","strings","issues","frontend","svelte","sanity","astro","server","markdown","rendering","vue"]