Tighten remaining wrangler parser contracts and field types#3
Merged
Conversation
Bring the last two loose wrangler parsers in line with the "map or loudly reject" contract the d1/r2/services parsers follow: - Add parseKvNamespacesFromCfg (exported, unit-tested) replacing the inline kv_namespaces loop. It rejects a non-array table list, a non-table entry, and a missing/empty/non-string `binding` or `id` — previously `cfg.kv_namespaces || []` was cast and iterated, so a non-array gave a confusing error and a non-string binding/id was String()-coerced past the binding-name regex. - Reject a present-but-non-table [assets] block instead of letting asRecord() null it out and silently skip assets. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
The wrangler parsers validated several fields at runtime but typed them `unknown` because the validators weren't TS type predicates. Annotate isValidJsClassDeclarationName, isAdminAcceptableNs, and isReservedNs as `value is string` predicates (JSDoc only; runtime unchanged, regex/logic stays in sync with shared/ns-pattern.js), and capture the service entrypoint/ns in the same block as their validation so the narrowing flows. The parser results now type these as `string` instead of `unknown`: - ServiceBinding `entrypoint` / `ns` - Durable Object and Workflow `className` - export `entrypoint` Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
There was a problem hiding this comment.
Pull request overview
This PR is a follow-up tightening of the Wrangler config parsing/typing layer in the WDL CLI deploy pipeline, aligning remaining loose parser contracts with the repo’s “validate or loudly reject” approach while improving JSDoc-driven type narrowing under tsc --strict.
Changes:
- Introduces and unit-tests
parseKvNamespacesFromCfg, and uses it inpackWranglerProjectinstead of ad-hoc iteration/casting. - Makes
[assets]fail fast when present but not a table (instead of being silently ignored). - Converts several validators to JSDoc type predicates so validated fields narrow from
unknowntostring(e.g., serviceentrypoint/ns, DO/workflowclassName, exportentrypoint).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/unit/cli-deploy.test.js | Adds focused unit coverage for parseKvNamespacesFromCfg validation behavior. |
| lib/wrangler/bindings.js | Adds KV namespace parser; tightens binding result types and uses predicate narrowing for validated string fields. |
| lib/wrangler-pack.js | Switches KV parsing to the shared parser and rejects non-table [assets] blocks. |
| lib/ns-pattern.js | Adds JSDoc type-predicate return annotations for validators to enable strict narrowing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Two PR-review follow-ups: - parseKvNamespacesFromCfg stored the untrimmed `id`, so "abc " passed validation but forwarded trailing whitespace. Store `.trim()`ed binding/id like the d1/r2 parsers; add coverage. - newClasses no longer needs Set<unknown> now that isValidJsClassDeclarationName is a `value is string` predicate and class_name narrows to string. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
The [assets]-must-be-a-table check ran during manifest assembly, after the wrangler dry-run bundle — so a malformed config bundled first and was also untestable via the standard "reject before bundling" pattern. Move it next to the other pre-bundle cfg-shape checks (name/main/vars) so it fails fast, and add a runDeployCommand test asserting it rejects before execFile is called. Per PR review feedback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
Now that parseWorkflowsFromCfg and parseServicesFromCfg return string-typed className/service/entrypoint/ns, reflect that in the manifest types instead of leaving them `unknown`: WorkerManifest.workflows[].className and the inline service-binding entry (service, entrypoint, ns) are now `string`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
Allowlist binding/id/preview_id and throw on any other [[kv_namespaces]] entry key, matching the d1/r2 parsers — so a typo or unsupported field is loudly rejected instead of silently ignored. preview_id (a `wrangler dev` concept WDL doesn't use) is accepted but ignored. Add coverage for both. Per PR review feedback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
`remote` (force `wrangler dev` to use real Cloudflare KV) is a valid Wrangler v4 local-dev field with no meaning for WDL deploy. Add it to the kv allowlist alongside preview_id so it's accepted but ignored (only binding/id are forwarded to the manifest); add coverage. Per PR review feedback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
Three test-section labels just restated what the assert arguments already show (trimmed values, missing/empty/non-string binding, missing/non-string id). Remove them; the comments that convey non-obvious intent (grammar still enforced, d1/r2 consistency, allow-but-ignore) stay. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9ZkKKDBaExknoWiERCDen
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.
Follow-up to the strict-typecheck PR (#2). Closes the two parser-consistency
gaps and the validated-but-
unknownfield types that were called out in thatreview but deferred to a focused PR.
Parser consistency — "map or loudly reject"
The d1/r2/services parsers reject malformed config loudly; the last two loose
spots now match:
kv_namespaces: replaced the inlinecfg.kv_namespaces || []loop with adedicated, exported, unit-tested
parseKvNamespacesFromCfg. It rejects anon-array list, a non-table entry, and a missing/empty/non-string
bindingorid. Previously the list was cast and iterated, so a non-array produced aconfusing error and a non-string
binding/idwasString()-coerced past thebinding-name regex.
assets: a present-but-non-table[assets]block is now rejected insteadof being nulled out by
asRecord()and silently skipping assets.Field types — tighten
unknown→stringSeveral parser results validate a field at runtime but typed it
unknownbecause the validators weren't TS type predicates. Annotate
isValidJsClassDeclarationName,isAdminAcceptableNs, andisReservedNsasvalue is stringpredicates (JSDoc only — runtime unchanged, regex/logic staysin sync with
shared/ns-pattern.js), and capture the serviceentrypoint/nsin the same block as their validation so the narrowing flows. These now type as
string:ServiceBindingentrypoint/nsclassNameentrypointChecks
npm run lint,npm run typecheck(strict),npm test, andnpm pack --dry-runall pass — 378/378 unit tests (newparseKvNamespacesFromCfgcoverage). Behavior change is limited to the two loud-reject hardenings above,
each of which only rejects input that was never valid.
🤖 Generated with Claude Code
Generated by Claude Code