Skip to content

feat: add hasICD10Code and harden lookup input handling - #291

Merged
lowlydba merged 2 commits into
mainfrom
lowlydba-harden-lookup-and-docs
Nov 6, 2026
Merged

lowlydba merged 2 commits into
mainfrom
lowlydba-harden-lookup-and-docs

Conversation

@lowlydba

@lowlydba lowlydba commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

What and why

A review of the package turned up input-handling gaps and missing docs. The agent-tool use case in the README makes the first one realistic: a model can call getICD10Description with a missing or non-string argument, and it threw a TypeError.

  • Non-string input returns undefined from lookups and "" from normalizeICD10Code
  • New hasICD10Code(code) returns a boolean, so callers don't need !== undefined
  • Lookups strip internal whitespace, so a pasted A00. 1 resolves
  • data/icd10.min.json is exported from the package. Importing it by path used to fail with ERR_PACKAGE_PATH_NOT_EXPORTED, which left bundlers and edge runtimes with only the CDN
  • README documents that category headers such as E11 aren't in the dataset, since the CMS file lists complete codes only. A grounding check would otherwise read a real category as a hallucinated code

Version is 3.2.0. The data is unchanged, and regenerating it with npm run parse-icd produces an identical file. The one behavior change consumers can see is the whitespace stripping: inputs like A 00.0 that returned undefined now resolve.

Testing

  • npm test
  • npm run lint

Added tests for non-string input, category headers, prototype property names, whitespace normalization, hasICD10Code, the data export through the packed tarball, and a format check on every dataset key and description.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment on lines +17 to +20
strategy:
fail-fast: false
matrix:
node-version: [22, 24]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: engines allows Node 22, but CI only ran 24. npm run test:coverage also passes locally on 22.23.4.

Comment thread README.md
- v3.1.0 - `ICD10_CM_RELEASE` export with the bundled fiscal year, effective date, and code count. Node.js 22+ declared in `engines`, `package.json` exported, expanded npm keywords. API, CDN, and versioning docs, plus contributing and security guides
- v3.0.0 - Data updated to the October 1, 2026 ICD-10-CM release (74,879 codes, from the FY2027 set)
- v2.0.0 - Data updated to the April 1, 2026 ICD-10-CM release (74,719 codes, from the FY2024 set). TypeScript 7 (native compiler) toolchain, ESLint replaced with oxlint + Prettier, dropped tsup for plain tsc (zero-bundler dual ESM/CJS), dataset shipped once and lazy-loaded (~50% smaller install, faster imports), fixed broken `require()` entry point
- v2.0.0 - Data updated to the April 1, 2026 ICD-10-CM release (74,719 codes, from the FY2026 set). TypeScript 7 (native compiler) toolchain, ESLint replaced with oxlint + Prettier, dropped tsup for plain tsc (zero-bundler dual ESM/CJS), dataset shipped once and lazy-loaded (~50% smaller install, faster imports), fixed broken `require()` entry point

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: the April 2026 release is the FY2026 set, not FY2024. Inferred from the 74,719 code count, so worth checking against the CDC files.

Comment thread scripts/parseICD.ts
Comment on lines +17 to 44
const codePattern = /^[A-Z][0-9A-Z]{2,6}$/;
const linePattern = /^(\S+)\s+(.+?)\s*$/;

// Function to parse the ICD file content
const parseICDFile = (content: string): ICD10Dictionary => {
const lines = content.split("\n"); // Split content into lines
const dictionary: ICD10Dictionary = {};

for (const line of lines) {
const [code, ...descriptionParts] = line.split(/\s+/); // Split by whitespace (tab or spaces)
const description = descriptionParts.join(" "); // Rejoin the description parts
for (const [index, line] of content.split(/\r?\n/).entries()) {
if (line.trim() === "") {
continue;
}

const match = linePattern.exec(line);
const [, code, description] = match ?? [];

if (code && description) {
dictionary[code] = description.trim(); // Add to dictionary
if (!code || !description || !codePattern.test(code)) {
throw new Error(
`Line ${index + 1} is not "<code> <description>": ${line}`,
);
}

if (Object.hasOwn(dictionary, code)) {
throw new Error(`Line ${index + 1} repeats code ${code}`);
}

dictionary[code] = description;
}

return dictionary;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: the parser now throws on a malformed line or a repeated code instead of skipping or overwriting it. The pattern allows letters in the second character because FY2027 added QA-prefixed codes. Output for the current source is byte-identical.

Comment thread tsconfig.json
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noUncheckedIndexedAccess": true,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: makes the dataset lookup in src/index.cts type as string | undefined instead of relying on the declared return type.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The parser accepts whitespace-only descriptions and writes blank values into the generated dataset, violating the trimmed, non-empty description invariant.

1 open issue
What changed in this PR

This PR adds hasICD10Code and hardens input handling in the ICD-10-CM lookup package without changing the bundled dataset.

Changes:

  • Adds the boolean lookup helper, non-string input handling, and internal whitespace normalization.
  • Exports the dataset JSON and documents category-header behavior.
  • Strengthens parser validation, expands tests, and adds Node.js 22/24 CI coverage.
File Description
tsconfig.json Enables unchecked indexed-access checking.
tests/​package.test.cjs Tests packaged helper, JSON export, and types.
tests/​index.test.cjs Covers input handling, helper behavior, and data integrity.
tests/​esm.test.mjs Checks the helper’s ESM export.
src/​index.mts Re-exports hasICD10Code for ESM consumers.
src/​index.cts Adds the helper and hardens normalization.
scripts/​parseICD.ts Validates records and anchors the output path to the script.
README.md Documents new behavior and corrects release-history text.
package.json Exports dataset JSON and bumps version to 3.2.0.
package-lock.json Synchronizes the package version.
.github/​workflows/​test.yml Runs CI on Node.js 22 and 24.

Review effort: Balanced

Comment thread scripts/parseICD.ts Resolved
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lowlydba
lowlydba marked this pull request as ready for review November 6, 2026 04:59
@lowlydba
lowlydba merged commit 08f797d into main Nov 6, 2026
5 checks passed
@lowlydba
lowlydba deleted the lowlydba-harden-lookup-and-docs branch November 6, 2026 04:59
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