install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/effect-platform" ~/.claude/skills/comeonoliver-skillshub-effect-platform && rm -rf "$T"
manifest:
skills/TerminalSkills/skills/effect-platform/SKILL.mdsource content
Effect Platform — Production TypeScript Framework
You are an expert in Effect (Effect-TS), the production-grade TypeScript framework for building reliable, composable applications. You help developers write type-safe code with structured error handling (typed errors in the type signature), dependency injection, concurrency, retries, scheduling, streaming, schema validation, and built-in observability — replacing scattered libraries (zod, retry, pino, awilix) with a single coherent platform.
Core Capabilities
Typed Errors and Effects
import { Effect, pipe } from "effect"; import { Schema } from "@effect/schema"; // Typed errors — compiler knows exactly what can fail class UserNotFound extends Schema.TaggedError<UserNotFound>()("UserNotFound", { userId: Schema.String, }) {} class DatabaseError extends Schema.TaggedError<DatabaseError>()("DatabaseError", { message: Schema.String, }) {} class ValidationError extends Schema.TaggedError<ValidationError>()("ValidationError", { field: Schema.String, reason: Schema.String, }) {} // Function signature tells you: returns User, can fail with UserNotFound | DatabaseError function getUser(id: string): Effect.Effect<User, UserNotFound | DatabaseError> { return pipe( Effect.tryPromise({ try: () => db.users.findUnique({ where: { id } }), catch: (e) => new DatabaseError({ message: String(e) }), }), Effect.flatMap((user) => user ? Effect.succeed(user) : Effect.fail(new UserNotFound({ userId: id })), ), ); } // Compose effects — errors propagate and accumulate in the type function getUserProfile(id: string): Effect.Effect< UserProfile, UserNotFound | DatabaseError | ValidationError // Compiler tracks all possible errors > { return pipe( getUser(id), Effect.flatMap((user) => getPreferences(user.id)), Effect.map((prefs) => ({ ...user, preferences: prefs })), ); } // Handle errors exhaustively const program = pipe( getUserProfile("user-42"), Effect.catchTags({ UserNotFound: (e) => Effect.succeed({ error: "User not found", userId: e.userId }), DatabaseError: (e) => Effect.die(e), // Unrecoverable — crash ValidationError: (e) => Effect.succeed({ error: `Invalid ${e.field}: ${e.reason}` }), }), ); // Run const result = await Effect.runPromise(program);
Dependency Injection (Layers)
import { Effect, Context, Layer } from "effect"; // Define service interfaces class Database extends Context.Tag("Database")<Database, { readonly query: (sql: string) => Effect.Effect<any[], DatabaseError>; }>() {} class EmailService extends Context.Tag("EmailService")<EmailService, { readonly send: (to: string, subject: string, body: string) => Effect.Effect<void>; }>() {} // Use services in your effects function createUser(data: CreateUserInput) { return Effect.gen(function* () { const db = yield* Database; const email = yield* EmailService; const [user] = yield* db.query("INSERT INTO users ..."); yield* email.send(data.email, "Welcome!", "Thanks for signing up"); return user; }); } // Provide implementations via Layers const DatabaseLive = Layer.succeed(Database, { query: (sql) => Effect.tryPromise({ try: () => pool.query(sql), catch: (e) => new DatabaseError({ message: String(e) }) }), }); const EmailLive = Layer.succeed(EmailService, { send: (to, subject, body) => Effect.promise(() => resend.emails.send({ to, subject, html: body })), }); // Compose and run const AppLayer = Layer.merge(DatabaseLive, EmailLive); await Effect.runPromise(pipe(createUser(input), Effect.provide(AppLayer)));
Concurrency and Retries
import { Effect, Schedule } from "effect"; // Retry with exponential backoff const reliableCall = pipe( callExternalApi(), Effect.retry( Schedule.exponential("100 millis").pipe( Schedule.compose(Schedule.recurs(5)), // Max 5 retries Schedule.jittered, // Add randomness ), ), ); // Concurrent execution with limits const results = yield* Effect.forEach( userIds, (id) => fetchUserData(id), { concurrency: 10 }, // Max 10 concurrent ); // Race — first to complete wins const fastest = yield* Effect.race( fetchFromPrimary(query), fetchFromReplica(query), ); // Timeout const withTimeout = pipe( longRunningTask(), Effect.timeout("5 seconds"), );
Installation
npm install effect @effect/schema @effect/platform
Best Practices
- Typed errors — Errors in the type signature; compiler enforces exhaustive handling; no forgotten catch blocks
- Effect.gen — Use generator syntax for readable async-like code;
instead ofyield*await - Layers for DI — Define services with
; swap implementations for testing without mocksContext.Tag - Schema validation — Use
instead of Zod; integrates with Effect error channel@effect/schema - Retry policies — Use
for retry, backoff, jitter; declarative, composableSchedule - Concurrency — Use
withEffect.forEach
option; bounded parallelism built-inconcurrency - Streaming — Use
for processing large datasets; backpressure, transformation, batchingStream - Observability — Built-in tracing and metrics via OpenTelemetry; no additional instrumentation code