docs/LIBRARIES.md
Libraries
Read this before you search node_modules or the web for how a library works. Each section says where the library's docs and types are installed, then lists mistakes agents made more than once. Installed code beats this file, the skills and the reference checkouts. When one of them is wrong, fix it in the PR that finds out.
Checked on 2026-10-12 against effect 4.0.1, solid-js and @solidjs/web 2.0.0-rc.13, alchemy 2.0.0-beta.81, drizzle-orm and drizzle-kit 1.0.0-rc.5-ab785fc, Panda 2.1.1, vite-plus 1.0.0 and better-auth 1.7.7. Manifests hold the current versions.
Finding installed source
- Resolve a package from the workspace that owns the code. Alchemy and Better Auth are in
apps/api/node_modules,infra/node_moduleshas its own Alchemy, Solid and Panda are inapps/web/node_modules, drizzle is inpackages/db/node_modules. The root holds Effect, Vite+ and the lint tools. A script run from/tmpresolves a different copy. - Do not build
.pnpm/<hash>paths or use a file finder that skips gitignored folders.rg -n 'name' apps/api/node_modules/alchemy/srcworks as written. For a transitive package,ls -d node_modules/.pnpm/@solidjs+signals@*. - Never edit a file under
node_modules. pnpm hard-links it from the shared store, so the edit reaches every worktree (ViteBuildChildRunner.jshad 24 links). Copy the file to/tmpor usepnpm patch. - Alchemy ships
srcandlibbut no docs. Read https://alchemy.run/llms.txt (an index of pages) or thealchemy-run/alchemyrepo underwebsite/src/content/docsat the installed tag.
Effect
Read node_modules/effect/AGENTS.md, then the examples in node_modules/effect/ai-docs/src/<topic>/ before you grep src. Topics: 01_effect (basics, schema, services, errors, resources, running), 03_stream, 04_integration (ManagedRuntime), 05_batching, 06_schedule, 08_observability (logging, OTLP tracing), 09_testing, 40_sql, 50_http-client, 51_http-server, 70_cli.
Types are the flat node_modules/effect/dist/*.d.ts (there is no dist/dts); sources are in node_modules/effect/src. Subpath modules are effect/rpc, effect/http, effect/sql, effect/cli, effect/observability, effect/encoding/Base64Url and effect/encoding/Hex.
Other packages: @effect/platform-node in apps/api, @effect/sql-d1 in packages/db and @effect/vitest for tests, all 4.0.1. @effect/tsgo 0.48.1 is the typecheck and language service, set under plugins in each tsconfig.json; its rules are listed in node_modules/@effect/tsgo/README.md.
Names that moved since 4.0.0-rc. The old form fails or does not exist.
| Old or guessed | Use |
|---|---|
effect/unstable/rpc, /http, /sql, /cli | effect/rpc, effect/http, effect/sql, effect/cli |
effect/Encoding, decodeBase64Url | effect/encoding/Base64Url decode, which returns a Result |
Config.string, .boolean, .redacted | Config.String, Config.Boolean, Config.Redacted, plus NonEmptyString, URL, Port |
Config.mapOrFail, Config.nonEmpty | Neither exists. Refine with Config.schema(codec, name) |
Schema.decodeUnknown | Schema.decodeUnknownEffect |
Schema.isLengthBetween | Schema.isBetweenLength |
Schema.TaggedErrorClass | Schema.TaggedError<Self>()("Tag", fields) |
Effect.either, effect/Either | Effect.result and effect/Result |
Effect.ignoreLogged | Effect.ignore({ log: true }) for typed failures, Effect.ignoreCause({ log: true }) to swallow defects as well |
Exit.isInterrupted | Cause.hasInterruptsOnly(exit.cause). Exit.hasInterrupts is a self is Failure guard that narrows else to never |
YieldWrap | Gone. Effects are iterable directly |
Schema.decodeUnknownEffect(schema)(input)fails withSchema.SchemaError.schema.makeEffect(input)(typed constructor input) fails withSchemaIssue.Issue. Map one to the other withnew Schema.SchemaError(issue).- Forking is
Effect.forkChild,forkDetachandforkScoped; useforkScopedfor work a layer owns.Effect.catchAllandEffect.forkare gone;Effect.catchreplacescatchAll. TestClockiseffect/testing/TestClock, not the root barrel.- Class-style definitions take
typeof Xin type helpers.RpcClient.FromGroup<typeof Api, RpcClientError>has the RPC names;FromGroup<Api>haskeyofofnever. One agent reported this as a library bug. Effect.promiseturns a rejection into a defect, andEffect.ignoredoes not catch defects. UseEffect.tryPromisewith a handler when the promise can reject.ConfigProvider.fromEnvtreats an empty string as missing, soConfig.option(Config.String("X"))isNoneforX=. PasspreserveEmptyStrings: trueto keep it.- In
Effect.gen, writereturn yield* new MyError(...)and make every branch return a value. That satisfies both themissingReturnYieldStarcheck and theconsistent-returnlint. strictEffectProvideflagsEffect.provideoutside an entry point. The 19 warnings invp checkare entry points and tests. Do not add more.- Row defaults such as
createdAtcome from SQLunixepoch, whichTestClockdoes not move. Pass times fromClock.currentTimeMilliswhen a test compares against them. - Run scratch scripts inside a workspace so they resolve the pinned
effect. From/tmp, bun picked up another copy and failed with missing exports.
Solid 2
Read apps/web/node_modules/solid-js/CHEATSHEET.md first. It matches the installed rc.13; the skill's references/ target rc.8. Types are in apps/web/node_modules/solid-js/types and apps/web/node_modules/@solidjs/web/types. Repair guides for diagnostic codes are in apps/web/node_modules/solid-js/skills/reactivity-diagnostics/SKILL.md and apps/web/node_modules/@solidjs/diagnostics/skills/agent-loops/SKILL.md. @solidjs/router is 2.0.0-next.35 and @solidjs/meta is 1.0.0-next.2; neither ships docs beyond a README.
createEffect(compute, apply)takes two functions. Lint names the replacement for each removedsolid-jsimport. The cheatsheet's "What changed from 1.x" lists the rest:onMountisonSettled,on()andcreateComputedare gone,ErrorBoundaryisErrored.<For each>rows are plain values and the index is an accessor. Withkeyed={false}orkeyed={fn}the row is an accessor.<Show>and<Match>function children get an accessor unlesskeyed. Read an accessor in JSX, a memo or an effect, never at the top of the callback.- A list rebuilt from fresh objects on every change trips
HOT_SCOPE_TIME. Project it by id withcreateProjection(fn, [], { name })(seeapps/web/src/marketing/PriceCalculator.tsx). Addingkeyeddid not help. <Dynamic>is deprecated. Usedynamic(() => Component).clientOnly(() => import(...))renders only the fallback on the server (seeapps/web/src/App.tsx).@solidjs/metahas noMetaProvider. ImportTitle,Meta,Linkdirectly.- The
solid/reactivitylint wantsonClick={() => props.dismiss()}, notonClick={props.dismiss}. - Effect reads and mutations go through
docs/SOLID-EFFECT-BINDING.md.Schema.Classinstances do not survive Solid's SSR serializer; sendSchema.Structdata.
Common repairs, by code:
STRICT_READ_UNTRACKED,PENDING_ASYNC_UNTRACKED_READ: a reactive read outside a tracking scope (destructured props, component body). Read it in JSX, a memo or an effect's compute half;untrack()if a one-time snapshot is the intent.REACTIVE_WRITE_IN_OWNED_SCOPE,ACTION_CALLED_IN_OWNED_SCOPE: a write,refresh()or action call during a component body or memo. Move it to an event handler or effect callback.FLUSH_IN_ACTION,FLUSH_IN_EFFECT_CALLBACK: delete theflush(). In tests, callflush()before reading the DOM.NO_OWNER_EFFECT,NO_OWNER_CLEANUP: create effects andonCleanupunder a component orcreateRoot.UNSTABLE_MEMO_OUTPUT: the memo returns fresh but equal objects. Return stable references or passequals.HOT_SCOPE_*,HUGE_FAN_OUT: a hot signal or one shared value read by many rows. Derive a slower value, or keep the answer in a store keyed by id.EFFECT_WRITES_OWN_SOURCE,EFFECT_RELAY_TEAR: an effect that writes derived state. UsecreateMemo.IMMUTABLE_UPDATE_IN_STORE,UNSTABLE_LIST_IDENTITY: mutate the store draft, orreconcile(data, "id"), and key<For>by a stable id.ASYNC_WATERFALL: read every async source before using any, so they start together.SILENT_HOLD,LONG_HOLD: add feedback withisPending,latestor an optimistic write. Never remove the hold; past 500 ms show aLoadingfallback.
Diagnostics in component tests (captureArtifact from @solidjs/diagnostics):
toHaveNoDiagnostics()filters by code and ignores severity, so aninforecord fails it.OPTIMISTIC_REVERTEDisinfoand fires for the documentedcreateOptimistic(false)saving flag. Tests allow it with a comment.HOT_SCOPE_TIMEis a wall-clock budget of 8 ms of compute per 1000 ms window that shared CI runners overshoot at random.apps/web/vitest-setup.tsturns it off for everycaptureArtifact(#183). Do not restructure markup to satisfy it.- Pick
maxRerunsby running once with a large budget, readingartifact.attribution.reruns.length, then setting the budget to that count.
Alchemy
Read apps/api/node_modules/alchemy/src (lib is the compiled copy that runs). Key files, all under src:
- D1:
Cloudflare/D1/{Database,QueryDatabase,QueryDatabaseLocal,QueryDatabaseHttpClient,ApplyMigrations}.ts. - Workers:
Cloudflare/Workers/{Worker,WorkerBridge,RuntimeBindings,RateLimit,WorkerName}.ts. Vite builds:Cloudflare/Website/Vite.ts,Cloudflare/Workers/{ViteChild,ViteBuildChildRunner}.ts,Cloudflare/Workers/Sources/Vite.ts. - Other resources:
Cloudflare/R2/,Cloudflare/Email/,Cloudflare/StateStore/,Axiom/{Dataset,Telemetry,ApiToken}.ts. - Core:
Stack.ts(evalStack),Stage.ts,RuntimeContext.ts,Auth/andCloudflare/Auth/(profiles and credentials),Cli/commands/. @alchemy.run/better-authsource is inapps/api/node_modules/@alchemy.run/better-auth/src.
Credentials. A profile is a file per provider in ~/.alchemy/profiles/<profile>/<provider>.json, chosen with --profile or ALCHEMY_PROFILE (default default), so every worktree on this box shares it. Providers fall back to environment variables: CLOUDFLARE_API_TOKEN with CLOUDFLARE_ACCOUNT_ID, and AXIOM_TOKEN with AXIOM_ORG_ID. CI uses those. Providers resolve credentials when the stack builds, so a provider in providers that has none fails every plan. That is why infra/alchemy.run.ts adds Axiom only for production and staging.
State lives in a Cloudflare Secrets Store. A token without Secrets Store access fails at "Computing plan" with a bare 403. The first production deploy also needed Account API Tokens edit (Alchemy mints the bucket-scoped R2 token), D1 edit, Workers scripts and routes, R2, Zone read and DNS edit.
- Leave Worker
nameunset.createWorkerNameuses an explicit name as written; otherwise it builds a stage-scoped name. - An expired Cloudflare login: run
cd infra && node ./node_modules/alchemy/bin/cli.js profile refresh --profile default --provider Cloudflarein a terminal on this box. - Read D1 from a script with
Cloudflare.D1.Database.ref("App", { stack, stage }),QueryDatabaseLocalandevalStack, asapps/api/scripts/stage.tsdoes. It uses your profile, notwrangler. RuntimeContext.phantomisLayer.emptytyped as providingRuntimeContext. Use it to discharge that requirement in code that only runs inside the Worker (a limiter call, a secret read). It supplies nothing at runtime. Background work usesCloudflare.WorkerExecutionContextandexecution.waitUntil(apps/api/src/index.ts).WorkerBridge.tsinstallsLogger.layer([Logger.consolePretty()])withoutmergeWithExisting, which drops Effect'stracerLogger.infrastructure/telemetry.tsaddsLogger.tracerLoggerback so log lines reach spans.ViteBuildChildRunnersetsdisableErrorReporting: truein beta.81, so a failed Vite build under Alchemy ends asBundleError: Vite build child exited with code 1:with nothing after it. Upstream prints the cause from 2026-10-06 (alchemy-run/alchemy#2036), after beta.81. Until you upgrade, reproduce withvp run --filter @tranzfer/web buildand read its output.- A green
vp run buildhas passed while the Alchemy deploy build failed (PR 101). The PR preview runs the real deploy build; read it before you merge. - Rate-limit
namespaceIdis unique per Cloudflare account and shared by every stage.bindingLimitsinapps/api/src/rate-limiter.tsis the one list of them; a duplicate id fails typecheck. alchemy state ls,catandrmbrowse and delete state records without touching cloud resources (https://alchemy.run/cli/inspecting-state). A deploy that failed mid-create left a record that broke the next plan on beta.79;alchemy state rm tranzfer/<stage>/<Resource>cleared it.
drizzle and D1
drizzle-orm and drizzle-kit are pinned to 1.0.0-rc.5-ab785fc in the pnpm catalog because Alchemy peers on that exact version. A different version puts a second Alchemy in the lockfile. Source is in packages/db/node_modules; docs are at https://orm.drizzle.team/llms.txt.
- Generate migrations with
cd packages/db && vp run generate --name <slug>.vp run generate -- --name xfails with "Unrecognized options" because Vite+ forwards the literal--. - Migration folders are timestamped and each
snapshot.jsonnames its parents inprevIds. Two branches that migrate in parallel fork the chain.vp exec drizzle-kit checkreports the fork. Delete your folder withgit rm -rand generate again after rebasing; never editsnapshot.json. - D1 has no interactive transactions.
Database.batchis the only atomic unit (packages/db/src/database.ts). One statement binds at most 100 parameters, so chunk multi-row inserts (apps/api/src/deliveries.ts). - Service tests use a local D1 through wrangler's
getPlatformProxy(packages/db/src/testing.ts). Do not importminiflare; its 5.x alpha changed its options.
Panda CSS
Config is apps/web/panda.config.ts. Token unions are in apps/web/styled-system/types/tokens.d.ts (regenerated by panda codegen on vp install). Docs are https://panda-css.com/llms.txt with pages get-started, styling, theming, design-systems and reference at https://panda-css.com/llms.txt/<page>, and everything at /llms-full.txt (about 700 KB, search it). The page list in the panda-css skill is older. vp exec panda cssgen --outfile /tmp/x.css shows the emitted CSS and prints extraction warnings.
- Use the repo's
fontSizetokens11 13 15 17 22 26 40, or[19px]. The preset names (xs,md) also typecheck but set no line height. strictTokensrejects raw values. Escape with[...], for exampleoutlineOffset: "[3px]"andfill: "[none]".- Inside
[...]Panda emits the text as written.[calc(100vh-64px)]and[0_0_0_1px_red]become invalid CSS and the browser drops them. Write real spaces:[calc(100vh - 64px)]. - A nested selector needs
&.css({ svg: {...} })is emitted as a property that never applies; write"& svg". transitionPropertysets only the property. AddtransitionDurationandtransitionTimingFunctionor nothing animates.translateXsets only--translate-x; addtranslate: "auto".- Breakpoints nest as
lgandlgDown._lgis not one. cxjoins strings. For a conditional class use Solid'sclass={[base, { on: flag() }]}.jsxStyleProps: "none"stays set. Without it<Meta content="...">is read as the CSScontentproperty and breaks the build.- Tokens go under
theme.extend. A barethemereplaces the preset's tokens. ListpresetBaseandpandaPresetinpresets.
Vite+ (vp)
Docs are local: node_modules/vite-plus/docs and node_modules/vite-plus/AGENTS.md.
vp <name>is a built-in command.vp run <name>runs apackage.jsonscript or task. The "running vp check as a built-in command" note is noise.- Do not put
--before script flags:vp run generate --name x. vp exec <bin>finds bins in the current directory'snode_modules/.bin. From the rootvp exec alchemyfails; frominfra/it works.vp installruns each workspaceprepare, which writesstyled-systemandapps/web/file-routes.d.ts. Run it in every new worktree beforevp check.vp check --fixformats, fixes and prints what is left. Run it once.- Vite+ bundles vitest 5.0.1. The catalog pins
vitestto match so there is one test runner.
Uppy
Source is apps/web/node_modules/@uppy/{core,aws-s3}/lib. patches/@uppy__aws-s3@6.2.2.patch adds partConcurrency (default 1) and makes ListParts follow NextPartNumberMarker; docs/STACK.md mentions only the second. Port it with pnpm's patch flow on every Uppy bump.
apps/web/src/uploads/uploads.tssetspartConcurrencyto 6 because R2 speaks HTTP/1.1 and Chromium opens six connections per origin. Six fill them; more would queue behind the same connections.uppy.upload()retries every errored file on the instance (Uppy.js:1491), not only the one you asked about.
Better Auth
Source is apps/api/node_modules/{better-auth,@alchemy.run/better-auth}. Docs index: https://better-auth.com/docs/llms.txt.
- Hooks and plugin endpoints are Promise callbacks outside the request fiber. Run an Effect from one with
runAuthCallbackinapps/api/src/infrastructure/auth.ts. It carries the request's services and abort signal. Do not add a secondAsyncLocalStorage. testUtilshas no HTTP route, soapps/api/src/infrastructure/staging-login.tsis our own key-gated sign-in.
Browser-only packages in the Worker bundle
@trev.zip/solid-toast exports browser, node and default but no worker or workerd condition, and its browser build calls createToaster() when imported. Import it only through import("../ui/Toasts") or clientOnly. The same applies to any client library imported from a server-rendered module. solid-js itself has a worker condition.
filesystem-routing writes apps/web/file-routes.d.ts from src/routes. prerender-crawler runs inside the Vite build in hybrid mode and emits only pages that emitPages accepts (apps/web/vite.config.ts); its README is in apps/web/node_modules/prerender-crawler.