Declare zod as a peer dependency only, and fail loudly on duplicate installs - #22
Open
brainkim wants to merge 1 commit into
Open
Declare zod as a peer dependency only, and fail loudly on duplicate installs#22brainkim wants to merge 1 commit into
brainkim wants to merge 1 commit into
Conversation
…nstalls zod was listed in both `dependencies` and `peerDependencies`. That is self-contradictory — the peer contract says the host provides zod, while the `dependencies` entry lets a second copy be installed alongside it. The drivers in this package already use the correct pattern (devDependency + peer), so zod is now brought in line: @b9g/zen has zero runtime dependencies. Two consequences of the duplicate, both fixed here: 1. Bundle size (#20). Consumers could bundle their zod *and* ours. The zod locale tree-shaking bug is upstream (colinhacks/zod#5561) and not ours to fix, but shipping a second copy of zod was. 2. Silent schema corruption. ddl.ts maps columns with `core instanceof z.ZodString` and friends. Under a duplicate install those checks compare against the *other* copy's classes and all fail, falling through to the "unknown type" branch — which types every column as TEXT. No error, no warning: numbers, booleans, dates and JSON would all quietly become TEXT. The fallback is no longer a trapdoor. A schema that advertises vendor "zod" via Standard Schema but fails `instanceof z.ZodType` can only come from a duplicate install, so it now throws a TableDefinitionError naming the cause. Zod types we genuinely don't map (ZodUnion, etc.) still pass instanceof and still fall back to TEXT, so the guard cannot false-positive. Also corrects the Standard Schema error message, which claimed any "Standard Schema-compliant library" would work. It won't: validation goes through ~standard, but DDL generation introspects Zod specifically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Vvtr747kUp1NobqCBeZJi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes part of #20.
The bug
zodwas declared in bothdependenciesandpeerDependencies(^4.0.0in each). That's self-contradictory: the peer contract says the host provides zod, while thedependenciesentry lets a second copy be installed alongside it. The drivers in this same manifest (better-sqlite3,postgres,mysql2) already use the correct pattern — devDependency + peer — so zod is now brought in line.@b9g/zennow has zero runtime dependencies.Why it matters
1. Bundle size (#20). Consumers could bundle their zod and ours — the nested copy is visible in the paste on #20 (
node_modules/@b9g/zen/node_modules/zod/...). The locale tree-shaking bug itself is upstream (colinhacks/zod#5561) and not ours to fix, but shipping a second copy of zod was.2. Silent schema corruption — the serious one.
ddl.tsmaps columns withcore instanceof z.ZodStringand friends. Under a duplicate install, those checks compare against the other copy's classes and all fail, falling through to:So every column silently becomes
TEXT— numbers, booleans, dates, JSON. No error, no warning. You'd get a quietly wrong schema and find out much later.The guard
That fallback is no longer a trapdoor. A schema that advertises
vendor: "zod"via Standard Schema but failsinstanceof z.ZodTypecan only have come from a duplicate install, so it now throws aTableDefinitionErrorthat names the actual cause.Crucially this cannot false-positive: Zod types we genuinely don't map (
ZodUnion, etc.) still passinstanceof z.ZodTypeand still take theTEXTfallback. Pinned by tests.Also
Corrects the Standard Schema error message, which claimed any "Standard Schema-compliant library" would work. It won't — validation goes through
~standard, but DDL generation introspects Zod specifically. The message now says so, and points at the peer dependency.Verification
bun test— 597 pass / 0 fail (3 new)npm run test:node— passestsc --noEmit— cleaneslint— clean🤖 Generated with Claude Code