You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Exploratory vision for v1: keep the user-facing strict layout tree small and intentional, and move Next.js codegen artifacts out of env/ into tool-owned virtual modules under root .arkenv/.
Supersedes the file-based direction in #1304 as the preferred end state. Related: #1307 / #1401 (Nuxt virtual alias), #1403 (Next auto-extend track), ADR 0016, #1109 (dot-folder rule — not reversed).
Problem
Next.js strict layout still requires manual server wiring and puts generated factories beside editable schemas:
#1304 proposed generating server.gen.ts beside env.gen.ts to fix that. Design review found deeper issues with putting more machine output under env/:
env.gen.ts + server.gen.ts naming is asymmetric and easy to misread
generated/ + .gen is redundant marking
Colocating *.gen.ts next to editable schemas invites misclicks
env/internal/ + env/generated/ together makes env/ feel like a mini-package, not an env validator
Awkward-path guardrails (internal/shared.ts) are a weak substitute for an explicit client-exposure API
Locked decisions
1. Phase out internal/shared.ts (concept shift)
Adapt the flat-layout exposeToClient: [...] array into strict layout. Client-safe variables are tagged on the single source of truth (e.g. env/server.ts) instead of living in a separately importable shared file. Exact transition / relationship to #1448 still under human mapping (ready for human).
2. On-disk location: root .arkenv/ ✅
Generated factories write to a root .arkenv/ directory (gitignored; regenerate via withArkEnv / arkenv generate).
Not.next/cache/arkenv — Next’s .next/cache is an ephemeral build-performance cache (CI Build Caching); the ecosystem routinely wipes .next / .next/cache to troubleshoot. Notnode_modules/.arkenv/ — brittle for monorepos / edge (Prisma’s lesson).
Meta-framework parallel: .nuxt/, .svelte-kit/ are first-class tool codegen dirs; host bundler caches are a different job.
#1109 defense (canonical):#1109 forbids importable code in dot-folders. Because public access is via virtual specifiers, .arkenv/ is cache-only backing — users never import from ".arkenv/...". This does not reverse #1109.
Scaffolding: CLI must add .arkenv/ to .gitignore.
3. Module resolution via withArkEnv
Codegen outputs physical files under .arkenv/; withArkEnv maps the public specifier to that path and injects Webpack resolve.alias + Turbopack resolveAlias. User next.config stays clean.
4. CI typecheck
Scaffold "postinstall": "arkenv generate" so tsc --noEmit succeeds before next build runs the plugin.
5. Security guardrail
Generated server artifacts inject "server-only"; so accidental Client Component imports fail the App Router build.
6. No generated server factory (ADR 0016)
Server DX follows #1403 (Nuxt-style auto-extend on @arkenv/nextjs/server), not arkenv/gen/server / server.gen.ts. Client virtual factory remains for Next’s runtimeEnv tax only.
Summary
Exploratory vision for v1: keep the user-facing strict layout tree small and intentional, and move Next.js codegen artifacts out of
env/into tool-owned virtual modules under root.arkenv/.Supersedes the file-based direction in #1304 as the preferred end state. Related: #1307 / #1401 (Nuxt virtual alias), #1403 (Next auto-extend track), ADR 0016, #1109 (dot-folder rule — not reversed).
Problem
Next.js strict layout still requires manual server wiring and puts generated factories beside editable schemas:
#1304 proposed generating
server.gen.tsbesideenv.gen.tsto fix that. Design review found deeper issues with putting more machine output underenv/:env.gen.ts+server.gen.tsnaming is asymmetric and easy to misreadgenerated/+.genis redundant marking*.gen.tsnext to editable schemas invites misclicksenv/internal/+env/generated/together makesenv/feel like a mini-package, not an env validatorinternal/shared.ts) are a weak substitute for an explicit client-exposure APILocked decisions
1. Phase out
internal/shared.ts(concept shift)Adapt the flat-layout
exposeToClient: [...]array into strict layout. Client-safe variables are tagged on the single source of truth (e.g.env/server.ts) instead of living in a separately importable shared file. Exact transition / relationship to #1448 still under human mapping (ready for human).2. On-disk location: root
.arkenv/✅Generated factories write to a root
.arkenv/directory (gitignored; regenerate viawithArkEnv/arkenv generate).Not
.next/cache/arkenv— Next’s.next/cacheis an ephemeral build-performance cache (CI Build Caching); the ecosystem routinely wipes.next/.next/cacheto troubleshoot. Notnode_modules/.arkenv/— brittle for monorepos / edge (Prisma’s lesson).Meta-framework parallel:
.nuxt/,.svelte-kit/are first-class tool codegen dirs; host bundler caches are a different job.#1109 defense (canonical):
#1109forbids importable code in dot-folders. Because public access is via virtual specifiers,.arkenv/is cache-only backing — users neverimport from ".arkenv/...". This does not reverse #1109.Scaffolding: CLI must add
.arkenv/to.gitignore.3. Module resolution via
withArkEnvCodegen outputs physical files under
.arkenv/;withArkEnvmaps the public specifier to that path and injects Webpackresolve.alias+ TurbopackresolveAlias. Usernext.configstays clean.4. CI typecheck
Scaffold
"postinstall": "arkenv generate"sotsc --noEmitsucceeds beforenext buildruns the plugin.5. Security guardrail
Generated server artifacts inject
"server-only";so accidental Client Component imports fail the App Router build.6. No generated server factory (ADR 0016)
Server DX follows #1403 (Nuxt-style auto-extend on
@arkenv/nextjs/server), notarkenv/gen/server/server.gen.ts. Client virtual factory remains for Next’sruntimeEnvtax only.Vision (v1) — directional
User-facing tree (target)
Machine-owned client factory
Desired properties:
.arkenv/collapsed / searchable-exclude friendly).gensuffix in the public import pathruntimeEnvbehaviorRelationship to #1307 / #1403
env/client.ts.arkenv/extendsextendsruntimeEnvcodegen out ofenv/Relationship to #1304
#1304 closed as superseded for end-state (in-tree server factory). This RFC + #1403 are the preferred v1 shape.
Open questions (still
ready for human)arkenv/gen/clientor@arkenv/nextjs/gen/client); exact string deferred to implementation PR. Optional cross-host rename with Nuxt#arkenv/…later../generated/env.genimports in v1, or hard cut?exposeToClient↔ strict transition — how fully it replacesinternal/shared/ Make env/internal/shared.ts optional in strict layout (dev/v0) #1448; docs & scaffold mapping.On-disk location— locked: root.arkenv/(gitignore + regenerate).Generated server factory / cross-host gen/server— out: #1403 + ADR 0016.Non-goals (for this RFC)
server.gen.tsas the final design.next/cacheornode_modules/Success sketch
env/is user-authored only (no sibling*.gen.ts).arkenv/, reached only via virtual import#1109remains intact (cache-only + virtual import, not hidden app modules)extends(via (v1) Auto-extend client env in Next.js strict layout via virtual module alias #1403).gitignorefor.arkenv/, andpostinstallgenerate