Skip to content

Fix issues found during the connection-reuse review - #56

Merged
jonathan-kosgei merged 8 commits into
mainfrom
fix-noticed-issues
Nov 3, 2026
Merged

jonathan-kosgei merged 8 commits into
mainfrom
fix-noticed-issues

Conversation

@jonathan-kosgei

@jonathan-kosgei jonathan-kosgei commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #55 that fixes the issues noticed during that review: the cache key ignored the requested fields, LookupResponse lagged the API, two cache tests checked the wrong instance, the README badge named a workflow that does not exist, bulkLookup() validated fields inside an empty if, npm run lint rewrote files in CI, requests carried no library User-Agent, and the development dependencies had open audit advisories. No version change; every public API change is additive, and every behavioural change comes with tests (58 become 74).

Changes

  • Cache keys include the selection. lookup(ip, selectField) and lookup(ip, undefined, fields) results were cached under the bare IP address and served to a later full lookup of the same address, and the other way round; bulkLookup(ips, fields) did the same. Full lookups keep the address as their key (ipdata.cache.get('1.1.1.1') still works), a single field is keyed address/field and a field list address?fields=a,b with the fields sorted, so the same selection in another order shares one entry. Bulk entries whose response has no ip (a fields list that leaves it out) are returned but no longer cached. Parameters are validated before the cache is consulted, so an invalid field now throws even when the address is cached. The README's Caching section describes the keying.
  • LookupResponse follows the API. New threat.is_vpn?: boolean, threat.scores?: { vpn_score, proxy_score, threat_score, trust_score } and region_type?: string | null; city, region, region_code, postal and every time_zone value are typed nullable, as the API sends them for addresses without a known location; count is typed string, which is what the API has always returned. Documented under Response Fields in the README.
  • Cache tests with a custom ttl test their own instance. The two "should not cache the information" tests read the shared instance's cache, which was empty regardless; they now check the instance they used, wait for the entry to expire and verify that the next lookup is sent to the API again.
  • README badge. Points at .github/workflows/default.yml (the Lint and Test workflow) instead of a workflow named CI, so it renders again.
  • bulkLookup() fields validation. The empty if around the validation call is replaced by a plain call; the validators are now validateSelectField / validateFields and return nothing instead of true.
  • Check-only lint. npm run lint is eslint "src/**" without --fix, so the lint job in both workflows no longer rewrites files; npm run lint:fix applies the automatic fixes locally. The workflow files are unchanged.
  • User-Agent header. Every request sends User-Agent: ipdata-node/<version>, with the version read from package.json at runtime; the file ships with every install, so this needs no build step and no new dependency. Tests match the header on the GET of lookup() and the POST of bulkLookup().
  • Development dependency advisories. npm audit fix (without --force; package.json unchanged) updates the transitive tooling dependencies that have fixed releases: @babel/core and helpers, baseline-browser-mapping, brace-expansion, browserslist, flatted, handlebars, js-yaml, minimatch 3.1.5 / 9.0.9, picomatch, ts-jest. Every high and critical advisory is cleared; the two that remain are listed below.
  • README: HTTP/2 through a proxy dispatcher. undici's ProxyAgent and EnvHttpProxyAgent take the ALPN protocols of the TLS connection tunnelled through the proxy from requestTls, so negotiating HTTP/2 with the API needs requestTls: { allowH2: true } next to allowH2: true; the dispatcher section says so and the proxy example shows it.

Not changed

  • Two audit advisories remain because neither has a fix compatible with jest 29 and eslint 8: deepmerge >=4.2.0 (through jest-config; the only fix is a downgrade to jest 25) and minimatch <11.0.1 (fixed only in minimatch 11, which eslint 8 and jest 29 do not use). npm lists every package that depends on them, so npm audit now shows 38 low/moderate entries where it showed 16 before, with 0 high and 0 critical.
  • region_type is not added to the selectField / fields allow-list: the type change reflects the field in full responses, and the sample responses do not show whether the API accepts it as a filter.
  • undici marks a kept-alive HTTP/1.1 connection reusable only on the event-loop turn after its response completed, so two lookups awaited strictly back-to-back may alternate between two pooled connections; this is undici behaviour, irrelevant over HTTP/2, and the existing loopback test documents it.
  • tsconfig.base.json still targets ES2015 with lib: ["es2015"]; changing the compile target is unrelated to these fixes and deserves its own change.
  • The pre-existing import/no-named-as-default lint warning on the test file's import line is untouched.

Verification

Node v22.23.4, npm 10.9.9, run on the final tree before pushing:

Command Result
rm -rf node_modules && npm ci exit 0
npm run build exit 0
npm run lint 0 errors, 1 warning (pre-existing import/no-named-as-default)
npm test Tests: 74 passed, 74 total (58 before)
npm audit 0 critical, 0 high; only the two roots listed above remain (before: 1 critical, 6 high, 1 moderate, 8 low)
npm pack --dry-run 6 files: lib/*, LICENSE, README.md, package.json, so the runtime require('../package.json') resolves in the published layout

CI: Lint and Test (default.yml) for head e9d7766, both runs green at the first attempt with the jobs lint, test (20), test (22) and test (24): push run #34 https://github.com/ipdata/node/actions/runs/41640659264 and pull_request run #35 https://github.com/ipdata/node/actions/runs/41640843022. No follow-up commits were needed.

🤖 Generated with Claude Code

The cache was keyed by IP address alone, so the result of
lookup(ip, selectField) or lookup(ip, undefined, fields) was stored
under the bare address and returned by a later full lookup of the same
address, and a full result was returned for a later filtered lookup.
bulkLookup() with fields stored filtered results the same way.

Keys now include the selection: a full lookup keeps the address as its
key, a single field is keyed as address/field and a field list as
address?fields=a,b with the fields sorted, so the same selection in
another order shares one entry. Bulk entries whose response lacks the
ip field are returned but not cached, because they cannot be attributed
to an address. Parameters are validated before the cache is consulted,
and the empty if block around the fields validation in bulkLookup() is
replaced by a plain call. Tests cover both directions for lookup() and
bulkLookup(), and the README describes the keying.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The two "should not cache the information" tests performed the lookup
on an instance created with ttl: 1 but then read the cache of the
shared instance, which was empty regardless, so they passed without
checking anything. They now wait for the entry to expire, check the
cache of the instance they used and verify that the next lookup is sent
to the API again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The API returns threat.is_vpn, threat.scores (vpn_score, proxy_score,
threat_score and trust_score), a top-level region_type, count as a
string, and null for city, region, region_code, region_type, postal and
every time_zone value when an address has no known location. None of
this was declared.

LookupResponse now carries these fields: the new keys are optional, the
location fields are nullable and count is typed as the string the API
has always sent. The README documents them and the tests check them
against the sample responses, including the compile-time types.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Requests went out with undici's default User-Agent. Every request now
identifies the library as ipdata-node/<version>, with the version read
from package.json at runtime; the file ships with every install, so no
build step or additional dependency is needed. Tests match the header
on the GET of lookup() and the POST of bulkLookup().

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The badge referenced a workflow named CI, which does not exist, so it
rendered as broken. It now uses the badge of
.github/workflows/default.yml and links to that workflow's runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
undici's ProxyAgent and EnvHttpProxyAgent take the ALPN protocols of
the connection tunnelled through the proxy from requestTls, so
negotiating HTTP/2 with the API needs requestTls: { allowH2: true }
next to allowH2: true. The README dispatcher section says so and the
proxy example shows it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The lint script ran eslint --fix, so linting rewrote files in place,
also in CI where the result is discarded. npm run lint now reports
problems without touching files, which is what both workflows need,
and npm run lint:fix applies the automatic fixes locally.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
npm audit fix refreshes the transitive development dependencies that
have fixed releases within the ranges package.json already allows:
@babel/core and its helpers, baseline-browser-mapping, brace-expansion,
browserslist, flatted, handlebars, js-yaml, minimatch 3.1.5 and 9.0.9,
picomatch and ts-jest, among others. This clears every high and
critical advisory; package.json is unchanged.

Two advisories have no fix compatible with jest 29 and eslint 8:
deepmerge (through jest-config) and the remaining minimatch report,
which is only fixed in minimatch 11. npm lists every package that
depends on them, which is why the report now shows more, lower
severity entries than before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jonathan-kosgei
jonathan-kosgei merged commit 619ed8c into main Nov 3, 2026
8 checks passed
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