Skip to content

fix(varlock): defer process.env typing when another .d.ts declares ProcessEnv - #1086

Open
theoephraim wants to merge 6 commits into
mainfrom
cf-process-env-type-collision
Open

theoephraim wants to merge 6 commits into
mainfrom
cf-process-env-type-collision

Conversation

@theoephraim

Copy link
Copy Markdown
Member

wrangler types writes a NodeJS.ProcessEnv augmentation into worker-configuration.d.ts. TypeScript merges all declarations of that interface and requires every shared key to be identical, so varlock's augmentation could never coexist with it. A single optional item (FOO?: string vs wrangler's FOO: string) is enough to fail with TS2320, as are enums and booleans, which we type as literal unions. Under skipLibCheck: true (on in most Worker templates) the error is suppressed and whichever declaration TypeScript reaches first silently wins instead.

@generateTsTypes now defaults processEnv to none when another .d.ts next to the schema (or next to the generated file) already declares NodeJS.ProcessEnv, and the generated file says which file caused it. An explicit processEnv= still wins.

Detection targets the declaration rather than the platform: infra-as-code setups may have no wrangler config at all, and a project with no other ProcessEnv declaration has no conflict to avoid, so missing one is the safe direction. Our own generated files are skipped via the autogenerated banner, so a second @generateTsTypes writing into the same directory isn't mistaken for a foreign declaration. Only the head of each file is read, since wrangler puts ~1MB of runtime types after the env block and codegen runs on every load.

Nothing is lost for anyone using varlock-wrangler types: it feeds the schema's keys to wrangler, so they're all typed on process.env as strings, and ENV keeps the coerced types either way.

Verified with tsc, not just unit tests: a schema with an enum plus an optional item alongside a real-shaped worker-configuration.d.ts now typechecks clean under skipLibCheck: false, and flipping the same project to processEnv=strict reproduces TS2320.

…ocessEnv

wrangler types writes a NodeJS.ProcessEnv augmentation into
worker-configuration.d.ts. TypeScript merges all declarations of that
interface and requires every shared key to be identical, so ours could
never coexist with it: an optional item alone (FOO?: string vs
FOO: string) fails with TS2320, as do enums and booleans.

Default processEnv to none when another .d.ts next to the schema (or the
generated file) already declares ProcessEnv, and note which file caused
it at the top of the generated output. Detecting the declaration rather
than the platform covers infra-as-code setups with no wrangler config,
and misses nothing that was actually conflicting. Explicit processEnv=
still wins.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 14, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
varlock-website f350959 Commit Preview URL

Branch Preview URL
Sep 14 2026, 05:33 AM

@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

bumpy-frog

The changes in this PR will be included in the next version bump.

patch Patch releases

  • @varlock/native-helper-darwin 1.19.0 → 1.19.1
  • @varlock/native-helper-linux-arm64 1.19.0 → 1.19.1
  • @varlock/native-helper-linux-x64 1.19.0 → 1.19.1
  • @varlock/native-helper-win32-x64 1.19.0 → 1.19.1
  • varlock 1.19.0 → 1.19.1

Bump files in this PR

Click here if you want to add another bump file to this PR


This comment is maintained by bumpy.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The declaration detector can suppress valid process.env typing based on unrelated syntax in a neighboring declaration file.

Reviewed changes in f350959f: the new foreign NodeJS.ProcessEnv detection, its integration with TypeScript generation, public export, generated note, tests, documentation, and release entry.

  • Conflict detection: Scans neighboring .d.ts file heads while excluding the target output and Varlock-generated files.
  • Generation behavior: Defaults implicit processEnv generation to none when a conflict is found, while preserving explicit overrides and other generated types.
  • Coverage and guidance: Adds focused detector and generation tests, Cloudflare guidance, decorator reference details, and a patch bump.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

The two substring checks did not establish that ProcessEnv was declared
inside NodeJS, so `interface ProcessEnvExtra`, a ProcessEnv declared
after the namespace block, or a mention in a comment all read as a
conflict and would have silently dropped valid process.env typing.

Strip comments, then brace-match each `namespace NodeJS {` block and
require the interface inside it.
@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size

⚠️ grows the bundle by 15.4 KB (+0.3%)

Metric main This PR Δ
Total dist 4541.8 KB 4557.2 KB +15.4 KB (+0.3%)
JS 1720.4 KB 1726.0 KB +5.6 KB (+0.3%)
Sourcemaps 2709.6 KB 2719.4 KB +9.8 KB (+0.4%)
Type defs 111.7 KB 111.7 KB
Other 0.0 KB 0.0 KB

dist/ only; native binaries are versioned separately and not counted here.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The revised detector still lets TypeScript literals alter structural matching, so the original false-positive risk remains.

Reviewed changes since the prior Pullfrog review:

  • Scoped declaration matching: Replaced independent substring checks with a namespace-scoped brace walk and comment stripping.
  • Regression coverage: Added cases for separate declarations, comments, interface-name prefixes, and nested declare global declarations.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

else if (src[i] === '}') depth--;
i++;
}
if (PROCESS_ENV_INTERFACE.test(src.slice(bodyStart, depth === 0 ? i - 1 : undefined))) return true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The brace walk still treats string and template literal contents as TypeScript syntax. For example, declare namespace NodeJS { const marker: 'interface ProcessEnv'; } returns a conflict, while a '}' literal before a real declaration closes the namespace early, so this can still either suppress valid typing or miss the conflict.

Technical details
# Ignore literal contents while scanning declarations

## Affected sites
- packages/varlock/src/env-graph/lib/type-generation/detect-process-env-augmentation.ts:63-77 - comment stripping and raw brace counting leave string and template literal contents active.

## Required outcome
- Only declaration syntax can satisfy `interface ProcessEnv` or change namespace brace depth.
- Add regression cases for declaration-like text and braces inside literals.

@pkg-pr-new

pkg-pr-new Bot commented Sep 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

varlock

npm i https://pkg.pr.new/varlock@1086

@varlock/native-helper-darwin

npm i https://pkg.pr.new/@varlock/native-helper-darwin@1086

@varlock/native-helper-linux-arm64

npm i https://pkg.pr.new/@varlock/native-helper-linux-arm64@1086

@varlock/native-helper-linux-x64

npm i https://pkg.pr.new/@varlock/native-helper-linux-x64@1086

@varlock/native-helper-win32-x64

npm i https://pkg.pr.new/@varlock/native-helper-win32-x64@1086

commit: 4b8d4a5

…sEnv

A `'}'` literal type inside a `namespace NodeJS` block ended the brace
walk early, and a literal spelled `'interface ProcessEnv'` faked a
declaration. Strip string and template literals alongside comments
before scanning.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Comment stripping still treats delimiters inside literals as syntax, so the conflict detector can miss real ProcessEnv declarations.

Reviewed changes since the prior Pullfrog review in d1da98a5:

  • Literal-aware scanning: Added string and template literal removal before namespace and declaration matching.
  • Regression coverage: Added cases for declaration-like text and closing braces inside string literals.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Stripping comments before literals let a literal spelled '//' or '/*'
hide a real declaration after it; stripping literals first would let a
comment's apostrophe do the same. Fold both token classes into one regex
so a single left-to-right pass takes whichever opens first.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The source-order fix addresses the reported comment-delimiter cases, but the broader open literal-scanning concern remains partially unresolved: valid quoted literals with escaped newline continuations are not consumed by the regex, so declaration text inside them can still trigger a false conflict and silently suppress Varlock's process.env typing. The existing thread remains open rather than duplicating that finding inline.

Reviewed changes since the prior Pullfrog review: the scanner now consumes comments and literals in one source-ordered pass and adds regressions for delimiters inside literals and apostrophes inside comments.

  • Unified lexical scan: Combined comment and literal alternatives into one global expression so the earliest token opener wins.
  • Regression coverage: Added tests for // and /* inside string literals, plus apostrophes inside line comments.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

…egex

Replace the brace-matching namespace walk with a single regex requiring
`interface ProcessEnv` to open the `namespace NodeJS` block, which is
what every generator that writes one actually emits. `[^}]` cannot run
past the first member, so a ProcessEnv declared after the namespace
closes still cannot match.

This gives up detecting a declaration that is not the namespace's first
member. That is the fail-safe direction: a miss leaves today's behaviour
and surfaces as TS2320 with the documented processEnv=none fix, while a
false positive would silently drop valid typing.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The adjacency regex does not resolve the existing escaped-newline literal finding. A production-path reproduction still identifies a .d.ts where interface ProcessEnv occurs only inside a valid continued string literal, so the existing thread remains open rather than being duplicated inline.

Reviewed changes since the prior Pullfrog review: the detector was simplified to use one bounded namespace-to-interface regex, with an explicit miss bias for declarations it cannot classify confidently.

  • Replaced the brace walk: Matched namespace NodeJS and a nearby interface ProcessEnv in one expression that cannot cross a closing brace.

  • Documented intentional misses: Added coverage showing that a ProcessEnv declaration after a braced namespace member is left for TypeScript and the documented processEnv=none override.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

…nning

A backslash-newline pair ended the literal match, exposing the rest of
the literal as if it were syntax, so declaration text inside a continued
string literal read as a real ProcessEnv declaration. Let the escape
alternative take any character, newline included.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The LF continuation fix leaves the existing literal-scanning concern open for CRLF source files. TypeScript accepts a backslash followed by CRLF as a line continuation, but \\[\s\S] consumes only \r; the remaining \n terminates the quoted-literal match, and a production-path probe still falsely detected declaration text inside that literal.

Reviewed changes since the prior Pullfrog review in 3d189b0d: the scanner now keeps LF escaped-newline continuations inside quoted literals instead of exposing their contents.

  • Expanded escape matching: Changed quoted and template literal escape branches to consume any escaped character, including LF, and added a focused continued-string regression.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant