← Back to list
Server Factory (
Database Service (
Query Mutations (

single-or-array-pattern
by EpicenterHQ
Press shortcut → speak → get text. Free and open source. More local-first apps soon ❤️
⭐ 3,939🍴 262📅 Jan 23, 2026
Use Cases
⚡
Efficient Code Generation
Auto-generate boilerplate code to reduce development time.
🔍
Code Review Assistance
Analyze PR changes and suggest improvements.
🔧
Refactoring Suggestions
Suggest refactoring options to improve code quality.
🧪
Test Code Generation
Auto-generate unit tests and E2E tests.
SKILL.md
name: single-or-array-pattern description: 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. metadata: author: epicenter version: '1.0'
Single-or-Array Overload Pattern
Accept both single items and arrays, normalize internally, delegate to array-only implementation.
Quick Reference
// Option 1: Explicit overloads (cleaner IDE signatures)
function create(item: T): Promise<Result<T, E>>;
function create(items: T[]): Promise<Result<T[], E>>;
function create(itemOrItems: T | T[]): Promise<Result<T | T[], E>> {
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
return createInternal(items);
}
// Option 2: Union type (less boilerplate)
function create(itemOrItems: T | T[]): Promise<Result<void, E>> {
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
// ... implementation
}
The Structure
- Public API: Accepts
T | T[] - Normalization:
Array.isArray()check, wrap single in array - Internal function: Only handles arrays
// Public: flexible API
function createServer(clientOrClients: Client | Client[], options?: Options) {
const clients = Array.isArray(clientOrClients)
? clientOrClients
: [clientOrClients];
return createServerInternal(clients, options);
}
// Internal: array-only, all real logic here
function createServerInternal(clients: Client[], options?: Options) {
// Implementation only handles arrays
}
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/epicenter/src/server/server.ts)
function createServer(
client: AnyWorkspaceClient,
options?: ServerOptions,
): ReturnType<typeof createServerInternal>;
function createServer(
clients: AnyWorkspaceClient[],
options?: ServerOptions,
): ReturnType<typeof createServerInternal>;
function createServer(
clientOrClients: AnyWorkspaceClient | AnyWorkspaceClient[],
options?: ServerOptions,
) {
const clients = Array.isArray(clientOrClients)
? clientOrClients
: [clientOrClients];
return createServerInternal(clients, options);
}
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) => DbServiceErr({ message: `Error deleting: ${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
Score
Total Score
80/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
✓人気
GitHub Stars 1000以上
+15
✓最近の活動
1ヶ月以内に更新
+10
✓フォーク
10回以上フォークされている
+5
○Issue管理
オープンIssueが50未満
0/5
✓言語
プログラミング言語が設定されている
+5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon
