2026-02-14 04:19:02 +00:00
# opencode database guide
2025-06-03 16:38:48 +00:00
2026-02-14 04:19:02 +00:00
## Database
2025-06-04 17:12:13 +00:00
2026-02-14 04:19:02 +00:00
- **Schema**: Drizzle schema lives in `src/**/*.sql.ts` .
- **Naming**: tables and columns use snake*case; join columns are `<entity>_id` ; indexes are `<table>*<column>\_idx` .
- **Migrations**: generated by Drizzle Kit using `drizzle.config.ts` (schema: `./src/**/*.sql.ts` , output: `./migration` ).
- **Command**: `bun run db generate --name <slug>` .
- **Output**: creates `migration/<timestamp>_<slug>/migration.sql` and `snapshot.json` .
- **Tests**: migration tests should read the per-folder layout (no `_journal.json` ).
2026-03-11 18:18:58 +00:00
2026-03-18 17:34:36 +00:00
# opencode Effect rules
2026-03-11 18:18:58 +00:00
2026-03-18 17:34:36 +00:00
Use these rules when writing or migrating Effect code.
2026-03-11 18:18:58 +00:00
2026-03-18 17:34:36 +00:00
See `specs/effect-migration.md` for the compact pattern reference and examples.
2026-03-11 18:18:58 +00:00
2026-03-18 17:34:36 +00:00
## Core
2026-03-11 18:18:58 +00:00
- Use `Effect.gen(function* () { ... })` for composition.
2026-03-18 17:34:36 +00:00
- Use `Effect.fn("Domain.method")` for named/traced effects and `Effect.fnUntraced` for internal helpers.
- `Effect.fn` / `Effect.fnUntraced` accept pipeable operators as extra arguments, so avoid unnecessary outer `.pipe()` wrappers.
- Use `Effect.callback` for callback-based APIs.
2026-03-11 18:18:58 +00:00
- Prefer `DateTime.nowAsDate` over `new Date(yield* Clock.currentTimeMillis)` when you need a `Date` .
2026-03-18 17:34:36 +00:00
## Schemas and errors
- Use `Schema.Class` for multi-field data.
- Use branded schemas (`Schema.brand`) for single-value types.
- Use `Schema.TaggedErrorClass` for typed errors.
- Use `Schema.Defect` instead of `unknown` for defect-like causes.
- In `Effect.gen` / `Effect.fn` , prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches.
2026-03-11 18:18:58 +00:00
2026-03-26 00:19:24 +00:00
## Runtime vs InstanceState
2026-03-16 17:18:40 +00:00
2026-03-26 00:19:24 +00:00
- Use `makeRuntime` (from `src/effect/run-service.ts` ) for all services. It returns `{ runPromise, runFork, runCallback }` backed by a shared `memoMap` that deduplicates layers.
- Use `InstanceState` (from `src/effect/instance-state.ts` ) for per-directory or per-project state that needs per-instance cleanup. It uses `ScopedCache` keyed by directory — each open project gets its own state, automatically cleaned up on disposal.
- If two open directories should not share one copy of the service, it needs `InstanceState` .
- Do the work directly in the `InstanceState.make` closure — `ScopedCache` handles run-once semantics. Don't add fibers, `ensure()` callbacks, or `started` flags on top.
- Use `Effect.addFinalizer` or `Effect.acquireRelease` inside the `InstanceState.make` closure for cleanup (subscriptions, process teardown, etc.).
- Use `Effect.forkScoped` inside the closure for background stream consumers — the fiber is interrupted when the instance is disposed.
2026-03-16 17:18:40 +00:00
2026-03-18 17:34:36 +00:00
## Preferred Effect services
2026-03-16 17:18:40 +00:00
2026-03-18 17:34:36 +00:00
- In effectified services, prefer yielding existing Effect services over dropping down to ad hoc platform APIs.
- Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O.
- Prefer `ChildProcessSpawner.ChildProcessSpawner` with `ChildProcess.make(...)` instead of custom process wrappers.
- Prefer `HttpClient.HttpClient` instead of raw `fetch` .
- Prefer `Path.Path` , `Config` , `Clock` , and `DateTime` when those concerns are already inside Effect code.
- For background loops or scheduled tasks, use `Effect.repeat` or `Effect.schedule` with `Effect.forkScoped` in the layer definition.
2026-03-16 17:18:40 +00:00
2026-03-18 17:34:36 +00:00
## Instance.bind — ALS for native callbacks
2026-03-16 17:18:40 +00:00
2026-03-18 17:34:36 +00:00
`Instance.bind(fn)` captures the current Instance AsyncLocalStorage context and restores it synchronously when called.
2026-03-16 17:18:40 +00:00
2026-03-26 00:19:24 +00:00
Use it for native addon callbacks (`@parcel/watcher`, `node-pty` , native `fs.watch` , etc.) that need to call `Bus.publish` or anything that reads `Instance.directory` .
2026-03-16 17:18:40 +00:00
2026-03-18 17:34:36 +00:00
You do not need it for `setTimeout` , `Promise.then` , `EventEmitter.on` , or Effect fibers.
2026-03-16 17:18:40 +00:00
```typescript
const cb = Instance.bind((err, evts) => {
Bus.publish(MyEvent, { ... })
})
nativeAddon.subscribe(dir, cb)
```