Skip to content

feat: 1.1.0 compat shim — qs-backed deparam, original param#4

Merged
edwardsmit merged 2 commits into
mainfrom
phase-2/compat-shim-1.1.0
May 20, 2026
Merged

feat: 1.1.0 compat shim — qs-backed deparam, original param#4
edwardsmit merged 2 commits into
mainfrom
phase-2/compat-shim-1.1.0

Conversation

@edwardsmit
Copy link
Copy Markdown
Owner

Summary

Compat shim release. deparam now delegates parsing to qs with three local protections layered on top (Latin-1 fallback decoder, per-pair prototype/depth rejection, type coercion). param stays untouched as the original 2014 jQuery-traditional implementation.

The library API is unchanged. All 58 conformance cases stay green; the only difference visible to callers is the optional maxDepth parameter introduced in 1.0.0.

Why this shape

After probing qs / neoqs / picoquery for replacement fit (see PR conversation history), qs covers the parse side cleanly with 5 supporting lines (the ISO-8859 fallback decoder). The stringify side has no security upside from qs — param is a pure encoder with no attack surface — so it stays on the original code. Asymmetric value, asymmetric architecture.

The Latin-1 fallback is load-bearing for downstream Elasticsearch 1.7 usage (legacy producers emit Latin-1 percent-encoded querystrings; accents must reach ES intact).

Dependency change

  • qs ^6.15.2 added as a runtime dependency (was previously zero deps)
  • Probe devDeps (neoqs, picoquery, picoquery-v1) removed
  • Future qs CVE patches flow in automatically via Dependabot lockfile bumps in downstream projects; no republish of this library needed for routine patches

CI changes

  • New test-qs-latest job: installs qs@latest unsaved and reruns the conformance spec — early-warning if a future qs release would break the shim
  • floor job now installs qs into each Docker container before running smoke-floor.js (qs is a runtime dep, must be available on Node 0.10–16)

Test plan

  • All 58 conformance cases green on Node 18/20/22/24
  • Smoke test green on Node 0.10 through 16 via Docker
  • test-qs-latest job green against qs@latest
  • npm audit --omit=dev reports 0 vulnerabilities
  • Human gate review after CI green, before any tag / publish

Notes

  • Not deprecating 1.1.0. The shim is the answer; deprecating it would push users away from the maintained version.
  • After 1.1.0 ships, npm deprecate node-qs-serialization@1.0.0 will point users to 1.1.0 (1.0.0 carries the hand-rolled deparam without the ongoing qs patch stream).
  • param.js comments stripped per the "no reasons in files" preference; behavior unchanged.

@edwardsmit edwardsmit changed the title Phase 2: 1.1.0 compat shim (qs-backed deparam, original param) feat: 1.1.0 compat shim — qs-backed deparam, original param May 20, 2026
Edward Smit added 2 commits May 20, 2026 10:45
Phase 2 of the modernization plan. The library now delegates parsing to
qs (currently 6.15.2) while keeping the original jQuery-traditional
stringify implementation untouched. All 58 conformance cases stay green;
the only API change vs 1.0.0 is the optional maxDepth parameter
introduced in 1.0.0 (already documented).

Architecture:
- lib/param.js: unchanged from the 2014 implementation. Pure ES5, zero
  deps, no attack surface. Comments stripped (no functional change).
- lib/deparam.js: rewritten as a qs.parse wrapper with three local
  protections layered on top:
    - safeDecoder: ISO-8859 fallback via unescape() when decodeURIComponent
      rejects malformed UTF-8. Load-bearing for legacy Latin-1 query
      strings flowing through to Elasticsearch with accent preservation.
    - preFilter: per-parameter rejection of __proto__/constructor/prototype
      key segments and of paths exceeding maxDepth. qs only rejects two
      of the three dangerous keys natively, and qs handles depth via
      whole-input truncate/throw; we reject per-pair.
    - coerceWalk: post-parse type coercion (true/false/null/undefined
      strings, numerics) matching the 1.0.0 contract.

Dependencies:
- qs ^6.15.2 added as a runtime dependency (was previously zero).
- Probe devDeps (neoqs, picoquery, picoquery-v1) removed; the
  replacement-probe was a one-off experiment whose conclusion is
  recorded in PR history.

CI:
- New job: Conformance against qs@latest. Installs qs@latest unsaved
  and reruns the full conformance spec. Early-warning for any future qs
  release that would break the shim.
- Floor matrix now installs qs into the Docker container before running
  smoke-floor (qs is a runtime dep so it must be present on Node 0.10
  through 16).

Verified:
- Node 4 floor smoke green via Docker amd64 (qs@6.15.2 installs and
  runs cleanly down there).
- npm audit --omit=dev: 0 vulnerabilities.
- npm pack --dry-run: 7 files, same allowlist as 1.0.0.
@edwardsmit edwardsmit force-pushed the phase-2/compat-shim-1.1.0 branch from 833af5d to f242a8a Compare May 20, 2026 08:45
@edwardsmit edwardsmit merged commit ed6dcfe into main May 20, 2026
15 checks passed
@edwardsmit edwardsmit deleted the phase-2/compat-shim-1.1.0 branch May 20, 2026 08:46
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.

1 participant