一键导入
effect-v4-services
Effect v4 Service patterns — Context.Service, Layer composition, ManagedRuntime, dependency injection, and service testing with @effect/vitest.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Effect v4 Service patterns — Context.Service, Layer composition, ManagedRuntime, dependency injection, and service testing with @effect/vitest.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Meta-skill for @tmnl/effect-sui development: Effect v4/effect-smol patterns, Sui ontology, PTB/Flow/Query/package modules, localnet testing, Move publishing, diagnostics, and Nix mission-control tooling.
Effect v4 Atom patterns — reactive state management with effect-atom. Atom.make, Atom.family, Registry, runtime atoms, and React integration via useAtomValue.
Effect v4 Schema patterns — defining, validating, transforming, and serializing data with Schema. Covers v4 API, v3→v4 migration, classes, structs, unions, collections, transformations.
GetByShell desktop shell — niri WM integration, panel system, calendar widget, popover components, modal patterns, and Nix-based configuration.
Skill governance codemod — create, inspect, update, delete, adopt, and dogfood skills. The meta-skill that governs all skills with CRUD protocols, verification utils, and self-referential governance.
NX workspace architecture — monorepo structure, generators, plugins, boundaries, Effect v4 isolation, dependency constraints, and workspace-map navigation.
| name | effect-v4-services |
| description | Effect v4 Service patterns — Context.Service, Layer composition, ManagedRuntime, dependency injection, and service testing with @effect/vitest. |
| governed-by | metaskill |
up: none prereqs: none provides: services, layers, dependency-injection, managed-runtime, testing children: CHANGELOG.md governed-by: metaskill
Source of truth:
submodules/effect-smol/packages/effect/src/Context.tsMigration guide:submodules/effect-smol/MIGRATION.md
import { Context, Effect, Layer, ManagedRuntime } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<Array<unknown>, DatabaseError>
}>()(
"myapp/db/Database"
) {
static readonly layer = Layer.effect(
Database,
Effect.gen(function*() {
const query = Effect.fn("Database.query")(function*(sql: string) {
yield* Effect.log("Executing:", sql)
return [{ id: 1, name: "Alice" }]
})
return Database.of({ query })
})
)
}
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<Array<unknown>, DatabaseError>
}>()(
"myapp/db/Database",
{
make: Effect.gen(function*() {
const pool = yield* ConnectionPool
return {
query: Effect.fn("Database.query")(function*(sql: string) {
const conn = yield* pool.acquire()
return yield* conn.execute(sql)
})
}
})
}
) {}
const Database = Context.Service<{
query: (sql: string) => Effect.Effect<Array<unknown>>
}>("myapp/Database")
| v3 | v4 |
|---|---|
class Foo extends Context.Tag("Foo")<Foo, Shape>() {} | class Foo extends Context.Service<Foo, Shape>()("Foo") {} |
class Foo extends Effect.Service<Foo>()("Foo", { ... }) | class Foo extends Context.Service<Foo, Shape>()("Foo", { make: ... }) {} |
Auto-generated .Default layer | No auto .Default. Define .layer manually. |
Effect.Service with scoped + dependencies | Context.Service with make. Use Layer.provide for deps. |
Context.Tag as marker | Context.Service IS the tag/key |
Runtime<R> type | REMOVED. Use Context<R> directly |
Effect.provideService(Tag, impl) | Effect.provide(Layer.succeed(Tag)(impl)) |
Layer.scoped | Layer.effect (scoped semantics folded in) |
Layer.scopedDiscard | Layer.effectDiscard |
ServiceMap.* (beta.23) | Context.* (renamed in beta.44) |
fiber.services | fiber.context (renamed in beta.44) |
// Effectful construction (replaces Layer.scoped from v3)
const layer = Layer.effect(
Database,
Effect.gen(function*() {
return Database.of({ query: ... })
})
)
// Synchronous construction
const layer = Layer.sync(Database, () =>
Database.of({ query: ... })
)
// Simple value
const layer = Layer.succeed(Database)({
query: (sql) => Effect.succeed([])
})
// Multiple services from one effect (replaces Layer.effectServices)
const layer = Layer.effectContext(
Effect.gen(function*() {
const impl = { query: ... }
return Context.make(Database, impl)
})
)
// Lazy layer (evaluated once, shared)
const layer = Layer.suspend(() => conditionalLayer)
// provide: feeds deps, consumes them
const AppLayer = DatabaseLive.pipe(
Layer.provide(ConnectionPoolLive)
)
// provideMerge: feeds deps AND keeps them in output
const FullLayer = DatabaseLive.pipe(
Layer.provideMerge(ConnectionPoolLive)
)
// mergeAll: combine independent layers
const InfraLayer = Layer.mergeAll(
Database.layer,
Cache.layer,
Logger.layer
)
// yield* in Effect.gen (most common)
const program = Effect.gen(function*() {
const db = yield* Database
const cache = yield* Cache
return yield* db.query("SELECT 1")
})
// Provide and run
Effect.runPromise(
program.pipe(Effect.provide(AppLayer))
)
// Effect.runSyncWith for synchronous with services
const result = Effect.runSyncWith(services)(program)
// Effect.runForkWith for forking with services
const fiber = Effect.runForkWith(services)(program)
// Create
const ctx = Context.make(Database, dbImpl)
// Add more services
const ctx2 = Context.add(ctx, Cache, cacheImpl)
// Access (unsafe)
const db = Context.getUnsafe(ctx, Database)
// Merge
const combined = Context.merge(ctx1, ctx2)
// Check
Context.isContext(value)
import { ManagedRuntime } from "effect"
// Build a runtime from layers (for app edge / extension entry)
const runtime = ManagedRuntime.make(AppLayer)
// Run programs
await runtime.runPromise(program)
// Dispose (runs finalizers)
await runtime.dispose()
Use package + path for uniqueness:
"@tmnl/stx/StxFactory"
"@tmnl/codemode-metaskill/SkillDiscovery"
"myapp/db/Database"
import { describe, it } from "@effect/vitest"
import { Effect, Layer } from "effect"
describe("Database", () => {
it.effect("queries successfully", () =>
Effect.gen(function*() {
const db = yield* Database
const rows = yield* db.query("SELECT 1")
expect(rows).toHaveLength(1)
}).pipe(
Effect.provide(Database.layerTest)
)
)
})