Agent skill
single-or-array-pattern
Pattern for functions that accept either a single item or an array. Use when creating CRUD operations, batch processing APIs, or factory functions that should flexibly handle one or many inputs.
Install this agent skill to your Project
npx add-skill https://github.com/EpicenterHQ/epicenter/tree/main/.agents/skills/single-or-array-pattern
Metadata
Additional technical details for this skill
- author
- epicenter
- version
- 1.0
SKILL.md
Single-or-Array Pattern
Accept both single items and arrays, normalize at the top, process uniformly.
Quick Reference
function create(itemOrItems: T | T[]): Promise<Result<void, E>> {
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
// ... implementation works on items array
}
The Structure
- Accept
T | T[]as the parameter type - Normalize with
Array.isArray()at the top of the function - All logic works against the array — one code path
function createServer(clientOrClients: Client | Client[], options?: Options) {
const clients = Array.isArray(clientOrClients)
? clientOrClients
: [clientOrClients];
// All real logic here, working with the array
for (const client of clients) {
// ...
}
}
Naming Conventions
| Parameter | Normalized Variable |
|---|---|
recordingOrRecordings |
recordings |
clientOrClients |
clients |
itemOrItems |
items |
paramsOrParamsArray |
paramsArray |
When to Use
Good fit:
- CRUD operations (create, update, delete)
- Batch processing APIs
- Factory functions accepting dependencies
- Any "do this to one or many" scenario
Skip when:
- Single vs batch have different semantics
- Return types vary significantly
- Array version needs different options
Codebase Examples
Server Factory (packages/server/src/server.ts)
function createServer(
clientOrClients: AnyWorkspaceClient | AnyWorkspaceClient[],
options?: ServerOptions,
) {
const clients = Array.isArray(clientOrClients)
? clientOrClients
: [clientOrClients];
// All server setup logic directly here
const workspaces: Record<string, AnyWorkspaceClient> = {};
for (const client of clients) {
workspaces[client.id] = client;
}
// ...
}
Database Service (apps/whispering/src/lib/services/isomorphic/db/web.ts)
delete: async (recordingOrRecordings) => {
const recordings = Array.isArray(recordingOrRecordings)
? recordingOrRecordings
: [recordingOrRecordings];
const ids = recordings.map((r) => r.id);
return tryAsync({
try: () => db.recordings.bulkDelete(ids),
catch: (error) => DbError.MutationFailed({ cause: error }),
});
},
Query Mutations (apps/whispering/src/lib/query/isomorphic/db.ts)
delete: defineMutation({
mutationFn: async (recordings: Recording | Recording[]) => {
const recordingsArray = Array.isArray(recordings)
? recordings
: [recordings];
for (const recording of recordingsArray) {
services.db.recordings.revokeAudioUrl(recording.id);
}
const { error } = await services.db.recordings.delete(recordingsArray);
if (error) return Err(error);
return Ok(undefined);
},
}),
Anti-Patterns
Don't: Separate functions for single vs array
// Harder to maintain, users must remember two APIs
function createRecording(recording: Recording): Promise<...>;
function createRecordings(recordings: Recording[]): Promise<...>;
Don't: Force arrays everywhere
// Awkward for single items
createRecordings([recording]); // Ugly
Don't: Duplicate logic in overloads
// BAD: Logic duplicated
function create(item: T) {
return db.insert(item); // Duplicated
}
function create(items: T[]) {
return db.bulkInsert(items); // Different code path
}
// GOOD: Single implementation
function create(itemOrItems: T | T[]) {
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
return db.bulkInsert(items); // One code path
}
References
- Full article — detailed explanation with more examples
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
svelte
Svelte 5 patterns including runes ($state, $derived, $props), TanStack Query, SvelteMap reactive state, shadcn-svelte components, and component composition. Use when the user mentions .svelte files, Svelte components, or when using TanStack Query, fromTable/fromKv, or shadcn-svelte UI.
autumn
Integrate Autumn billing—define features/plans in autumn.config.ts, use autumn-js SDK for credit checks/tracking, manage the atmn CLI for push/pull. Use when working on billing, pricing, credits, plan gating, or metered usage.
handoff-prompt
Draft a self-contained implementation prompt that an agent can execute with zero prior context. Use when the user says "draft a prompt", "write a handoff", "make a prompt I can copy-paste", "create a delegation brief", or wants to hand off a task to another agent, tool, or conversation.
typebox
TypeBox and TypeMap patterns for runtime schema validation and JSON Schema generation. Use when the user mentions TypeBox, TypeMap, Standard Schema, or when working with runtime type validation, JSON Schema, or schema-based validation.
factory-function-composition
Apply factory function patterns to compose clients and services with proper separation of concerns. Use when creating functions that depend on external clients, wrapping resources with domain-specific methods, or refactoring code that mixes client/service/method options together.
progress-summary
This skill should be used when the user asks questions like "can you summarize", "what happened", "what did we do", "what's the situation", "where are we at", "explain what's going on", "give me an overview", "what's been done", "tell me about this", "walk me through what happened", or any question asking to understand the current state of work or changes. Provides conversational, PR-style summaries with visual diagrams.
Didn't find tool you were looking for?