Token导航 LogoToken导航TokenDH.com
前端设计需要联网github未标认证来源可访问clear审计通过

repository-pattern存储库模式

Agent Skill

用于围绕 GitHub 仓库、Issue、Pull Request、分支、提交和代码协作流程提供辅助能力。它适合让 Agent 查询项目状态、整理变更、辅助创建或检查协作事项,并把仓库中的信息转成可执行的下一步。使用时需要区分只读查询和写入操作;涉及创建 PR、修改 Issue、推送分支或访问私有仓库时,应确认 token 权限、目标仓库范围和用户授权。

总安装

297

周安装

12

GitHub Stars

12

下载量

93
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:repository-pattern(存储库模式)
来源仓库:https://github.com/alexanderop/workouttracker
仓库路径:skills/repository-pattern
安装命令:
npx skills add https://github.com/alexanderop/workouttracker --skill repository-pattern
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/alexanderop/workouttracker --skill repository-pattern

简介

repository-pattern 用于围绕 GitHub 仓库、Issue、Pull Request、分支和提交提供辅助能力。

  • 它适合查询项目状态、整理变更并支持协作事项操作。
  • 可把仓库信息转化为可执行的下一步建议。
  • 安装命令为 npx skills add https://github.com/alexanderop/workouttracker --skill repository-pattern。
  • 涉及写入操作时应确认 token 权限和仓库范围。

SKILL.md

Repository Pattern

Overview

This skill helps implement the repository pattern used in this workout tracker application. The pattern provides a clean abstraction over Dexie (IndexedDB) with type-safe interfaces, consistent error handling, and standardized CRUD operations.

Architecture Overview

Layered Approach:

Interfaces → Implementations → Provider → Public API
    ↓              ↓              ↓           ↓
db/interfaces  db/impl/dexie  db/provider  db/index

Flow:

  1. Define repository interface in db/interfaces.ts
  2. Implement with Dexie in db/implementations/dexie/[entity].ts
  3. Register in factory provider (db/implementations/dexie/index.ts)
  4. Export public getter (db/index.ts)
  5. Use in features via getEntityRepository()

Core Workflow

Follow these 6 steps when creating a new repository:

Step 1: Define Interface

Location: src/db/interfaces.ts

Define the repository interface with standard CRUD methods:

export type EntityRepository = {
  getAll(): Promise<ReadonlyArray<DbEntity>>
  getById(id: string): Promise<DbEntity | undefined>
  create(entity: Omit<DbEntity, 'id' | 'createdAt'>): Promise<DbEntity>
  update(id: string, updates: Partial<Omit<DbEntity, 'id' | 'createdAt'>>): Promise<void>
  delete(id: string): Promise<void>
  // ... entity-specific queries
}

Add to RepositoryProvider:

export type RepositoryProvider = {
  // ... existing
  entity: EntityRepository
}

Guidelines:

  • Use ReadonlyArray<T> and Readonly<T> for arguments
  • getById returns undefined (not throw) when not found
  • create returns the created entity with generated ID
  • update and delete return void
  • Use Omit<> to exclude auto-generated fields (id, createdAt)

Step 2: Add Schema Type

Location: src/db/schema.ts

Define database type with Db prefix:

/**
 * Entity stored in database.
 * Uses null instead of undefined for explicit "no value" semantics.
 */
export type DbEntity = {
  id: string
  name: string
  value: string | null        // Use null, not undefined
  createdAt: number
  updatedAt: number | null    // null until first update
}

Key Conventions:

  • Always use Db prefix for database types
  • Use null for "no value" (not undefined)
  • Store user input numbers as string (e.g., kg: string, reps: string)
  • Use discriminated unions with kind property for variants
  • Include type guards if needed: export function isDbEntity(x: unknown): x is DbEntity

Step 3: Update Database Class

Location: src/db/implementations/dexie/database.ts

Add table and indexes:

export class WorkoutTrackerDb extends Dexie {
  // ... existing tables
  entities!: Table<DbEntity, string>

  constructor() {
    super('WorkoutTracker')

    // Increment version number
    this.version(3).stores({
      // ... existing tables
      entities: 'id, name, createdAt',  // Index: primary + frequently queried fields
    })
  }
}

Indexing Guidelines:

  • Always index primary key (automatic)
  • Index fields used in where(), orderBy(), equals()
  • Index foreign keys for joins
  • Compound indexes for junction tables: '[field1+field2], field1, field2'

Step 4: Implement Repository

Location: src/db/implementations/dexie/[entity].ts

Create factory function returning repository implementation:

import type { EntityRepository } from '@/db/interfaces'
import type { DbEntity } from '@/db/schema'
import { createDatabaseError, tryCatch } from '@/lib/tryCatch'
import type { WorkoutTrackerDb } from './database'
import { generateId } from './database'

/**
 * Dexie implementation of EntityRepository.
 */
export function createDexieEntityRepository(db: WorkoutTrackerDb): EntityRepository {
  return {
    async getAll(): Promise<ReadonlyArray<DbEntity>> {
      const [error, entities] = await tryCatch(
        db.entities.orderBy('createdAt').reverse().toArray(),
      )
      if (error) {
        throw createDatabaseError('LOAD_FAILED', 'retrieve entities', error)
      }
      return entities
    },

    async getById(id: string): Promise<DbEntity | undefined> {
      const [error, entity] = await tryCatch(db.entities.get(id))
      if (error) {
        throw createDatabaseError('LOAD_FAILED', `retrieve entity with id ${id}`, error)
      }
      return entity
    },

    async create(
      entity: Omit<DbEntity, 'id' | 'createdAt'>,
    ): Promise<DbEntity> {
      const newEntity: DbEntity = {
        ...entity,
        id: generateId(),
        createdAt: Date.now(),
      }

      const [error] = await tryCatch(db.entities.add(newEntity))
      if (error) {
        throw createDatabaseError('SAVE_FAILED', 'create entity', error)
      }

      return newEntity
    },

    async update(
      id: string,
      updates: Partial<Omit<DbEntity, 'id' | 'createdAt'>>,
    ): Promise<void> {
      const [error, updatedCount] = await tryCatch(
        db.entities.update(id, {
          ...updates,
          updatedAt: Date.now(),  // Auto-inject timestamp
        }),
      )

      if (error) {
        throw createDatabaseError('SAVE_FAILED', `update entity with id ${id}`, error)
      }

      if (updatedCount === 0) {
        throw createDatabaseError('NOT_FOUND', `entity with id ${id} not found`)
      }
    },

    async delete(id: string): Promise<void> {
      const [error] = await tryCatch(db.entities.delete(id))
      if (error) {
        throw createDatabaseError('SAVE_FAILED', `delete entity with id ${id}`, error)
      }
      // Soft delete: no NOT_FOUND check
    },
  }
}

Key Patterns:

  • Use tryCatch() wrapper for all operations (preferred pattern)
  • Two-phase error checking: operation failure + not found
  • Auto-inject timestamps: createdAt, updatedAt
  • Use generateId() for new IDs
  • Soft delete: no error if entity doesn't exist

Step 5: Register in Factory Provider

Location: src/db/implementations/dexie/index.ts

Import and add to provider:

import { createDexieEntityRepository } from './entity'

export function createDexieRepositoryProvider(): RepositoryProvider {
  return {
    activeWorkout: createDexieActiveWorkoutRepository(db),
    workouts: createDexieWorkoutsRepository(db),
    // ... existing repositories
    entity: createDexieEntityRepository(db),  // ADD THIS
  }
}

Step 6: Export Public Getter

Location: src/db/index.ts

Add getter function:

export function getEntityRepository(): EntityRepository {
  return getRepositoryProvider().entity
}

Usage in Features

import { getEntityRepository } from '@/db'
import type { DbEntity } from '@/db/schema'

export function useEntities() {
  const entities = ref<ReadonlyArray<DbEntity>>([])
  const entityRepo = getEntityRepository()

  async function loadEntities() {
    entities.value = await entityRepo.getAll()
  }

  async function createEntity(name: string, value: string | null) {
    const newEntity = await entityRepo.create({ name, value, updatedAt: null })
    entities.value = [...entities.value, newEntity]
  }

  async function updateEntity(id: string, updates: Partial<DbEntity>) {
    await entityRepo.update(id, updates)
    await loadEntities()
  }

  async function deleteEntity(id: string) {
    await entityRepo.delete(id)
    entities.value = entities.value.filter(e => e.id !== id)
  }

  onMounted(() => loadEntities())

  return {
    entities: readonly(entities),
    createEntity,
    updateEntity,
    deleteEntity,
  }
}

Key Principles

1. Error Handling

Preferred: tryCatch wrapper

const [error, result] = await tryCatch(operation)
if (error) {
  throw createDatabaseError('ERROR_CODE', 'description', error)
}

Error codes:

  • SAVE_FAILED - Create, update, delete operations
  • LOAD_FAILED - Read operations
  • NOT_FOUND - Entity doesn't exist

2. Timestamp Management

Auto-inject timestamps in repository methods:

  • createdAt: Date.now() in create()
  • updatedAt: Date.now() in update()
  • lastUsedAt: Date.now() when accessing entity

3. ID Generation

Always use generateId() from database.ts:

import { generateId } from './database'

const newEntity = {
  ...entity,
  id: generateId(),  // crypto.randomUUID()
}

4. Soft Delete

Delete operations don't throw if entity doesn't exist:

async delete(id: string): Promise<void> {
  await tryCatch(db.entities.delete(id))
  // No NOT_FOUND check - silent success
}

5. Type Safety

  • Use Readonly<T> and ReadonlyArray<T> for function parameters
  • Use Omit<> to exclude auto-generated fields
  • Use discriminated unions with exhaustive checking
  • Define type guards for runtime type checking

File Reference

Critical files when creating repository:

  • src/db/interfaces.ts - Interface definition + RepositoryProvider
  • src/db/schema.ts - Db-prefixed type definitions
  • src/db/implementations/dexie/database.ts - Table + indexes
  • src/db/implementations/dexie/[entity].ts - Implementation
  • src/db/implementations/dexie/index.ts - Factory registration
  • src/db/index.ts - Public getter export

Utility imports:

  • @/lib/tryCatch - Error handling utilities
  • @/db/implementations/dexie/database - generateId()

Detailed References

For complete examples and advanced patterns, see:

- Example 1: Simple CRUD repository (Notes) - Example 2: Complex transformations (Tags with many-to-many) - Example 3: Extending Settings with function overloads

- Error handling patterns (direct throw vs tryCatch) - CRUD patterns (getAll, create, update, delete, timestamps) - Type transformation patterns (helper utilities, deep cloning) - Advanced patterns (function overloads, singleton, transactions, bulk ops) - Schema design patterns (discriminated unions, indexing, embedded vs referenced)

Common Scenarios

Scenario 1: Simple CRUD Repository

Need basic storage for an entity? See examples.md → Example 1 (Notes).

Quick checklist:

  1. Define interface with getAll/getById/create/update/delete
  2. Add DbEntity type with Db prefix
  3. Add table with indexes
  4. Implement using tryCatch pattern
  5. Register and export

Scenario 2: Complex Relationships

Need many-to-many relationships or complex queries? See examples.md → Example 2 (Tags).

Pattern: Junction table + transaction handling + usage tracking.

Scenario 3: Extending Settings

Adding new setting? See examples.md → Example 3.

Pattern: Add discriminated union member + function overload + default value.

Scenario 4: Conversions Between Types

Need to convert between templates and workouts? See patterns.md → Type Transformation Patterns.

Pattern: Helper utilities with exhaustive switch statements.

Scenario 5: Bulk Operations

Import/export or batch delete? See patterns.md → Advanced Patterns → Bulk Operations.

Pattern: Transactions + Promise.all() + bulkAdd().

Testing Support

Mock repositories for unit tests:

import { createMockRepositories } from '@/__tests__/helpers/mockRepositories'

const mockRepos = createMockRepositories()
mockRepos.entity.getAll.mockResolvedValue([...])

Integration tests with fake-indexeddb are automatically set up via test helpers.

Migration Strategy

When updating schema version:

  1. Increment version number in database.ts
  2. Add new table/indexes in .stores({})
  3. Dexie handles migrations automatically
  4. For data migrations, use .upgrade() callback
this.version(3)
  .stores({
    entities: 'id, name, createdAt',
  })
  .upgrade(tx => {
    // Optional data migration logic
    return tx.table('entities').toCollection().modify(entity => {
      entity.newField = 'default'
    })
  })

Project-Specific Repositories

Db* Types vs Domain Types

AspectDatabase (Db*)Domain
Filesrc/db/schema.tssrc/types/
PrefixDbWorkout, DbSetWorkout, Set
No valuenullundefined
Optimized forStorageApp logic

Available Repositories

SettingsRepository - Key-value store with defaults:

const repo = getSettingsRepository()
await repo.get('theme')           // 'light' | 'dark' | 'system'
await repo.get('defaultRestTimer') // number
await repo.set({ key: 'theme', value: 'dark' })
await repo.getAll()               // All settings merged with defaults
await repo.reset('theme')

CustomExercisesRepository - Exercise CRUD:

const repo = getCustomExercisesRepository()
await repo.getAll()
await repo.getById(id)
await repo.add({ id: generateId(), name: 'Squat', ... })
await repo.update(id, { name: 'Back Squat' })
await repo.delete(id)

WorkoutsRepository - Completed workouts:

const repo = getWorkoutsRepository()
await repo.getAll()
await repo.getById(id)
await repo.create(convertWorkoutToDb(workout))
await repo.delete(id)

ActiveWorkoutRepository - Singleton active workout:

const repo = getActiveWorkoutRepository()
await repo.load()
await repo.save(dbActiveWorkout)
await repo.delete()
await repo.exists()

BenchmarksRepository - Benchmark workouts:

const repo = getBenchmarksRepository()
await repo.getAll()
await repo.getById(id)
await repo.create({ id: generateId(), name: 'Fran', ... })
await repo.update(id, { name: 'Fran (Scaled)' })
await repo.delete(id)

TemplatesRepository - Workout templates:

const repo = getTemplatesRepository()
await repo.getAll()
await repo.getById(id)
await repo.create(template)
await repo.update(id, changes)
await repo.delete(id)

Using Converters

Always convert when crossing domain/database boundary:

import { convertWorkoutToDb, convertDbToWorkout } from '@/db/converters'

// Domain → Database
const dbWorkout = convertWorkoutToDb(workout)
await getWorkoutsRepository().create(dbWorkout)

// Database → Domain
const dbWorkout = await getWorkoutsRepository().getById(id)
const workout = convertDbToWorkout(dbWorkout)

Partial Updates with buildPartialUpdate

Dexie's update() overwrites all keys in the object. Use buildPartialUpdate to only modify provided fields:

import { buildPartialUpdate } from '@/db/partialUpdate'

const NULLABLE_FIELDS = ['equipment', 'muscle', 'image']

// Only includes keys present in updates
// Converts undefined → null for nullable fields
const dbUpdates = buildPartialUpdate(updates, NULLABLE_FIELDS)
await repo.update(id, dbUpdates)

Why: Without filtering, {name: 'Squat', equipment: undefined} would set equipment to null even if you only meant to update the name.

Project-Specific Gotchas

1. Use null in Database, undefined in Domain

IndexedDB doesn't support undefined:

// Database types
type DbExercise = {
  equipment: Equipment | null  // Use null
}

// Domain types
type Exercise = {
  equipment?: Equipment  // Use undefined
}

2. Always Reset Database in Tests

import { resetDatabase } from '@/__tests__/setup'

beforeEach(async () => {
  await resetDatabase()
})

3. Convert Types at Boundaries

// BAD - Type mismatch
await getWorkoutsRepository().create(workout)

// GOOD - Convert first
const dbWorkout = convertWorkoutToDb(workout)
await getWorkoutsRepository().create(dbWorkout)

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenCode

29.44%
按下载量换算27

Claude Code

24.09%
按下载量换算22

windsurf

19.29%
按下载量换算18

Codex

11.85%
按下载量换算11

Antigravity

7.07%
按下载量换算7

Gemini CLI

3.85%
按下载量换算4

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills