.claude/skills/database-drizzle/SKILL.md
Drizzle ORM schema patterns, D1 bulk inserts, and data model guidelines for Mana Vault. Use when modifying database schema, writing Drizzle queries, working with collection cards, virtual lists, or any server-side data operations. IMPORTANT: Always read SCHEMA.md before making data model changes.
npx skillsauth add WonderPanda/mana-vault database-drizzleInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
packages/db/src/schema/integer with mode: "timestamp_ms" for datesSCHEMA.md to understand the data model and relationshipsCloudflare D1 has a limit of ~100 bound variables per query. For bulk inserts, use the json_each + json_extract pattern: serialize the entire array as a single JSON string (1 bound variable), then use json_each() to expand rows and json_extract() to pull columns.
const CHUNK_SIZE = 100;
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
const chunk = items.slice(i, i + CHUNK_SIZE);
const jsonData = JSON.stringify(chunk);
await db.run(sql`
INSERT OR IGNORE INTO ${myTable} (id, name, value)
SELECT
json_extract(value, '$.id'),
json_extract(value, '$.name'),
json_extract(value, '$.value')
FROM json_each(${jsonData})
`);
}
Key points:
json_each(${jsonData}) expands the JSON array into rows with a value columnjson_extract(value, '$.field') pulls each field from the JSON objectINSERT OR IGNORE or INSERT OR REPLACE as neededExisting implementations:
apps/server/src/queue-handlers/scryfall-import.ts — Scryfall card importpackages/api/src/routers/collections.ts — collection card importWhen working with the core data model, keep these principles in mind:
Collection is the source of truth: collection_card represents cards the user physically owns. Each row = one physical card.
Lists are separate from Collection: virtual_list and virtual_list_card are staging areas and historical records. They reference cards but don't represent ownership.
Never auto-create collection cards: When importing to lists, only create virtual_list_card entries with scryfall_card_id. Collection cards are only created via explicit "move to collection" action.
Never delete collection cards from list operations: Deleting a list should only remove virtual_list and virtual_list_card entries. Collection cards are independent.
Soft deletes for collection cards: Use status field (owned/traded/sold/lost) instead of hard deletes to preserve history.
packages/env/@mana-vault/env/server@mana-vault/env/web@mana-vault/env/native.env files go in apps/server/.envdevelopment
Set up worktrunk (git worktree manager) with dynamic port allocation for Better-T-Stack monorepos using Doppler, Alchemy, and Vite. Use when bootstrapping a new project's worktree workflow, or when asked to "set up worktrunk", "configure worktrees", "add worktree support", or "set up dynamic ports for worktrees". Covers: post-create hooks (Doppler setup, dependency install, .env.worktree generation), env-based port overrides in Vite and Alchemy configs, and dev script modification to source worktree-specific env vars after Doppler injection.
tools
TanStack DB client-side reactive data patterns for Mana Vault. Use when working with useLiveQuery hooks, client-side collections, RxDB/Dexie sync, or any reactive data queries in the web app. Covers hook locations, DB setup, and query builder best practices.
development
React Native and Expo patterns for Mana Vault mobile app. Use when working on the native app in apps/native/, modifying Expo Router routes, HeroUI Native components, or Uniwind styling. Covers project structure and key conventions.
development
React component patterns, styling conventions, and UI guidelines for Mana Vault web app. Use when creating or modifying React components, working with TailwindCSS, shadcn/ui, class-variance-authority, or route components in TanStack Router.