Skip to content

fix(database-client): treat unreadable env vars as unset under Deno without --allow-env - #155

Merged
jamie-at-bunny merged 2 commits into
database-clientfrom
database-env-read
Aug 17, 2026
Merged

fix(database-client): treat unreadable env vars as unset under Deno without --allow-env#155
jamie-at-bunny merged 2 commits into
database-clientfrom
database-env-read

Conversation

@jamie-at-bunny

Copy link
Copy Markdown
Member

Stacked on #154.

Problem

readEnv guards Deno.env.get with a try/catch so a missing --allow-env degrades gracefully, then falls back to process.env. But Deno 2 ships a node-compat process global whose env reads throw the same NotCapable error, and that access was outside the guard. Running without env permission (e.g. deno run --no-prompt, or a granular --allow-env=OTHER_VAR) crashed with an uncaught NotCapable instead of reaching connect()'s clearer "no database URL: pass { url } or set BUNNY_DATABASE_URL" error.

Change

  • One module-scope sniff type covers both globals, and a single try/catch wraps both reads, so any permission throw reads as unset. Also collapses the two structural casts into one.
  • env.test.ts covers the process path, empty-string normalization, Deno-first precedence, empty-Deno fallthrough, and both throwing-global cases (throwing Deno.env.get stub, throwing process.env proxy).
  • Patch changeset; folds into the 0.0.1 release from feat(database): add @bunny.net/database-client, a fetch-only SQL client #154.

Verification

  • 57 tests pass, typecheck and build clean.
  • Live repro: deno run --no-prompt on a script calling readEnv crashed before and now prints undefined with exit 0; --allow-env still returns the value.

- remove the truncated sentence at the end of the security section
- fold the intro's dangling dependency claim into the sentence
- explain the unsafe-integer rejection in active voice
- collapse a stacked comment in env.ts to one line
- describe the API surface in AGENTS.md without the D1 comparison
…ithout --allow-env

Deno 2's node-compat process.env throws NotCapable on read just like
Deno.env.get, but only the latter was guarded, so readEnv crashed instead
of falling through to connect()'s clearer missing-URL error. One module-scope
sniff type and one try now cover both globals, and env.test.ts locks in the
degrade-to-unset behavior with throwing stubs.
@bunnynet-devops

Copy link
Copy Markdown

@codex review

@changeset-bot

changeset-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 15f25c0

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

This PR includes changesets to release 1 package
Name Type
@bunny.net/database-client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown

Greptile Summary

The PR makes database environment-variable lookup degrade to an unset value when Deno denies environment access, rather than leaking a permission exception.

  • Consolidates the Deno and Node-compatible environment reads under one guarded expression.
  • Adds coverage for precedence, empty values, missing values, and permission failures from either global.
  • Updates the package changeset and supporting documentation.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or non-blocking defects identified.

The guarded lookup preserves documented Deno-first precedence and empty-value normalization while ensuring permission failures resolve to the caller’s existing missing-configuration behavior, and the added tests cover both environment access paths.

Important Files Changed

Filename Overview
packages/database-client/src/env.ts Consolidates cross-runtime environment lookup and converts denied reads into an unset result without exposing a concrete regression.
packages/database-client/src/env.test.ts Adds focused tests for process lookup, Deno precedence, empty-value fallthrough, and throwing environment APIs.
packages/database-client/README.md Contains minor wording refinements with no behavioral or public-contract regression.
AGENTS.md Corrects the database-client API description from D1-shaped to prepared-statement based.
.changeset/database-env-read.md Correctly records the environment-read fix as a patch release.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[readEnv name] --> B{Deno value is readable and non-empty?}
    B -->|Yes| C[Return Deno value]
    B -->|No| D{process.env value is readable and non-empty?}
    D -->|Yes| E[Return process value]
    D -->|No| F[Return undefined]
    B -->|Read throws| F
    D -->|Read throws| F
Loading

Reviews (1): Last reviewed commit: "fix(database-client): treat unreadable e..." | Re-trigger Greptile

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 15f25c00a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jamie-at-bunny
jamie-at-bunny merged commit e0dd6c8 into database-client Aug 17, 2026
2 checks passed
@jamie-at-bunny
jamie-at-bunny deleted the database-env-read branch August 17, 2026 12:46
jamie-at-bunny added a commit that referenced this pull request Aug 18, 2026
Every runtime this client targets exposes process.env, Deno included, so
the Deno.env.get branch and the globalThis runtime sniff were carrying no
weight. readEnv() now reads process.env directly, and the examples use
process.env instead of importing readEnv.

The permission tolerance from #155 stays: reading can throw rather than
return undefined when Deno runs without --allow-env, so readEnv still
catches and reports the variable as unset. Verified that connect() with
no arguments under `deno run --allow-net` still raises URL_MISSING with
its usual message rather than a NotCapable crash.

--allow-env is still required for Deno to read process.env, so the
example's invocation line is unchanged.

Behaviour is unchanged for consumers, so the existing changeset still
covers it. Live smoke passes on Bun and Deno.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants