Skip to content

feat(medical): add a medical module - #3949

Open
rodrigobnogueira wants to merge 11 commits into
faker-js:nextfrom
rodrigobnogueira:feat/medical-module
Open

feat(medical): add a medical module#3949
rodrigobnogueira wants to merge 11 commits into
faker-js:nextfrom
rodrigobnogueira:feat/medical-module

Conversation

@rodrigobnogueira

@rodrigobnogueira rodrigobnogueira commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a new faker.medical module — the request in #2309. It generates plausible, non-clinical healthcare data for tests, demos, and fixtures:

Method Example
specialty() 'Cardiology'
department() 'Emergency Department'
condition() 'Type 2 Diabetes' (name only, no code)
symptom() 'Shortness of Breath'
procedure() 'Appendectomy'
allergen() 'Penicillin'
bloodType() 'O+'
drugName() 'Zolpraxen' (invented, screened)

Scoped to Faker's "plausible, not real" policy

Following the discussion in #2309 (thanks @ST-DDT / @matthewmayer), I deliberately excluded:

  • Real diagnosis codes (e.g. ICD-10) — WHO-copyrighted and not "plausible" data.
  • Real medicine names in generated drugsdrugName() draws from a static list of 245 invented names, pre-generated from neutral morphemes and screened against WHO INN generics and INN class stems (e.g. -statin, -pril), human brand names including ones marketed only outside the US and EU, and veterinary products (per @matthewmayer's suggestion to pre-generate rather than retry at runtime). Five names were removed after a second screen — Revalor and Orbaex shadowed FDA veterinary products, Lumemox a moxifloxacin eye drop sold in India and Kenya, Nuvizen a medication-device trademark, and Sonadex a further collision caught in the 11 August rescreen — and all five are now a denylist in the spec, alongside a test rejecting any name that ends in an INN stem, so the guarantee is enforced rather than described.
  • Note on allergen() — the allergen vocabulary intentionally names real substances (foods, environmental triggers, and medicines such as penicillin), drawn from commonly available public lists of frequent allergens: an allergen field describes what a person reacts to, so real names make the data more meaningful there. The fictitious rule applies to generated medication names, not to this descriptive vocabulary. Lactose, Fructose and MSG have been dropped, since the reactions to those are not immune-mediated and NIAID's guidelines say so explicitly for lactose.
  • Correlated composites (full patient records) — methods stay atomic; correlation is left to the user.

Notes

  • English locale data only for now — curated, deduped, and sorted.
  • Tests, snapshots, and JSDoc (@example, @since 10.6.0) included; ts-check, lint, test, and build are green locally.
  • The vocabulary was reshaped from an existing Python Faker healthcare provider to fit the constraints above.

Happy to adjust scope or naming (medical vs health) — feedback welcome.

Adds `faker.medical` with plausible, non-clinical healthcare data for tests,
demos, and fixtures: specialty, department, condition, symptom, procedure,
allergen, bloodType, and a fictitious drugName.

Scoped to Faker's plausible-not-real policy per the discussion in faker-js#2309:
real diagnosis codes (ICD-10), real medicine names, and correlated patient
records are deliberately excluded. drugName() assembles invented brand-style
names from morphemes and avoids real WHO INN stems, so it can never resemble a
real drug. English locale data; tests, snapshots, and JSDoc included.

Ref faker-js#2309
@rodrigobnogueira
rodrigobnogueira requested a review from a team as a code owner July 11, 2026 23:13
@netlify

netlify Bot commented Jul 11, 2026

Copy link
Copy Markdown

Deploy Preview for fakerjs ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 06c5318
🔍 Latest deploy log https://app.netlify.com/projects/fakerjs/deploys/6a8940a19b31e0000872d29f
😎 Deploy Preview https://deploy-preview-3949.fakerjs.dev
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.98%. Comparing base (636a913) to head (06c5318).

Additional details and impacted files
@@           Coverage Diff           @@
##             next    #3949   +/-   ##
=======================================
  Coverage   98.98%   98.98%           
=======================================
  Files         926      928    +2     
  Lines        3246     3264   +18     
  Branches      582      569   -13     
=======================================
+ Hits         3213     3231   +18     
  Misses         29       29           
  Partials        4        4           
Files with missing lines Coverage Δ
src/faker.ts 100.00% <100.00%> (ø)
src/locales/en/index.ts 100.00% <ø> (ø)
src/locales/en/medical/index.ts 100.00% <100.00%> (ø)
src/modules/medical/module.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- add generated docs/.vitepress/api-pages.ts and regenerate the typed
  en/medical locale index (fixes the Check Code Generation job)
- symptom: drop non-symptom trigger/modifier entries (Exercise, Laughing,
  Lifting, Relief with Movement, Worsening at Night, Tolerance), exact
  plural duplicates (Headaches, Red Patches, Sleep Disturbances), and
  entries duplicating the condition list (Anemia, Hyperlipidemia);
  clarify Frequency/Urgency as Urinary Frequency/Urinary Urgency
- specialty: use Allergy and Immunology; add Anesthesiology, Emergency
  Medicine, Family Medicine, Neurosurgery, Pathology, Plastic Surgery,
  Radiology
- procedure: replace the trademarked Botox Injection with Botulinum
  Toxin Injection
- condition: use a literal ö in Sjögren's Syndrome
- drug morphemes: swap pex/zia/se/ga for plex/vex/so/go so no generated
  name can contain an unfortunate substring; the full 31,500-name space
  is verified free of real brand names and WHO INN generics
Comment thread src/modules/medical/module.ts Outdated
Comment thread src/modules/medical/module.ts Outdated
@ST-DDT ST-DDT added c: feature Request for new feature p: 1-normal Nothing urgent s: pending triage Pending Triage labels Jul 12, 2026
@ST-DDT

ST-DDT commented Jul 12, 2026

Copy link
Copy Markdown
Member

Are you familiar with the medical sector?

@Shinigami92

Copy link
Copy Markdown
Member

Are you familiar with the medical sector?

Looks like he is the maintainer of https://github.com/rodrigobnogueira/faker-healthcare-provider 👀

…tic list

Per review feedback: drugName() now draws from a pre-generated
drug_name locale array (250 names built once from the morphemes and
screened against real brand names, WHO INN generic names, and INN
class stems), instead of assembling names at runtime with a retry
loop. The four morpheme definition keys are gone and the module reads
like every other array-backed method. Also trims the class-level
overview to not enumerate every method.
@rodrigobnogueira

rodrigobnogueira commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Software engineer, not a clinician 😄 — no clinical authority claimed. This module was deliberately scoped so nothing in it depends on clinical judgment — curated vocabulary only, no codes, no real medicines. I was starting a repo to port the python faker-healthcare-provider to a typescript package. Then I found this issue #2309 , so I gave up the idea of a new package and opened this PR. Hope it helps 🙏

rodrigobnogueira added a commit to rodrigobnogueira/faker-healthcare-provider that referenced this pull request Jul 12, 2026
The fictitious brand generator could form names containing offensive
substrings (256 of the 31,500 possible names): infix na + suffix zia
and prefixes ending ra + suffix pex among others. Swap suffixes
pex->plex and zia->vex, infixes se->so and ga->go, mirroring the fix
already shipped in the TypeScript port (faker-js/faker#3949).

Re-verified across the full 31,500-name space: zero offensive
substrings (28-term list), zero collisions with shipped INN generics
or well-known real brands, forbidden-ending retry trigger unchanged.
zh_CN inherits the base generator, so all six locales are covered.

Bump 2.3.0 -> 2.3.1.
ST-DDT
ST-DDT previously approved these changes Jul 13, 2026

@ST-DDT ST-DDT left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good implementation wise.
I have no knowledge in the medical domain.

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.

Pull request overview

Adds a new faker.medical module to generate plausible, non-clinical healthcare terms (specialties, departments, conditions, symptoms, procedures, allergens, blood types, and fictitious drug names), with English locale data and API docs integration.

Changes:

  • Introduces MedicalModule with eight methods wired into Faker.
  • Adds English locale definitions for the new medical category plus type definitions and locale generation wiring.
  • Adds module tests/snapshots and updates API docs navigation/completeness snapshots.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test/scripts/apidocs/snapshots/verify-jsdoc-tags.spec.ts.snap Registers medical module/methods in JSDoc completeness snapshot.
test/modules/medical.spec.ts Adds module-level tests for faker.medical.*.
test/modules/snapshots/medical.spec.ts.snap Adds seeded snapshot outputs for medical.
src/modules/medical/module.ts Implements MedicalModule methods + JSDoc.
src/modules/medical/index.ts Barrel export for the module.
src/locales/en/medical/allergen.ts Adds English allergen vocabulary.
src/locales/en/medical/blood_type.ts Adds blood type vocabulary.
src/locales/en/medical/condition.ts Adds condition vocabulary.
src/locales/en/medical/department.ts Adds department vocabulary.
src/locales/en/medical/drug_name.ts Adds fictitious drug name vocabulary.
src/locales/en/medical/procedure.ts Adds procedure vocabulary.
src/locales/en/medical/specialty.ts Adds specialty vocabulary.
src/locales/en/medical/symptom.ts Adds symptom vocabulary.
src/locales/en/medical/index.ts Aggregates medical locale definitions for en.
src/locales/en/index.ts Wires medical into the en locale definition.
src/index.ts Exports MedicalModule type from the public entrypoint.
src/faker.ts Instantiates/exposes faker.medical.
src/definitions/medical.ts Adds MedicalDefinition type.
src/definitions/index.ts Re-exports MedicalDefinition.
src/definitions/definitions.ts Adds optional medical to LocaleDefinition.
scripts/generate-locales.ts Registers medical for locale generation typing.
docs/.vitepress/api-pages.ts Adds Medical to the API docs sidebar.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/locales/en/medical/allergen.ts
Comment thread src/locales/en/medical/allergen.ts Outdated
Comment thread src/locales/en/medical/allergen.ts
Comment thread src/locales/en/medical/allergen.ts
Comment thread test/modules/medical.spec.ts
@ST-DDT ST-DDT linked an issue Jul 13, 2026 that may be closed by this pull request
@ST-DDT ST-DDT added this to the v11.0 milestone Aug 9, 2026
A rescreen of all 250 drug names found four that a reader could look up. The
original screen covered human brand names, WHO INN generics and INN stems,
which is why it missed them:

- Revalor and Orbax are FDA-approved veterinary products, and a name is a
  collision whether the real product treats people or animals. Orbaex differs
  from Orbax by one inserted letter, which is close enough to remove.
- Lumemox is a moxifloxacin eye drop marketed in India and Kenya, invisible to
  a screen that only queries the FDA and EMA registries.
- Nuvizen is a medication-device trademark rather than a medicine, removed
  because a reader cannot tell that distinction from the generated value.

The four are now a committed denylist that fails the suite if one returns, and
a second test rejects any name ending in an INN stem, so the constraint the
docs promise is checked rather than asserted.

Also drops Lactose, Fructose and MSG from allergen(). NIAID's food-allergy
guidelines state that lactose intolerance is non-immune and lactose is not an
allergen; fructose malabsorption is metabolic; and the FDA treats MSG as GRAS
with no reproducible immune mechanism. The remaining entries stay real on
purpose, which the method's documentation now explains rather than leaving to
the reader.

MedicalDefinition is exported from the package root, so
`import type { MedicalDefinition } from '@faker-js/faker'` resolves instead of
raising TS2305, and the module overview no longer claims real medicine names
are out of scope while allergen() deliberately returns them.
@rodrigobnogueira

rodrigobnogueira commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Rescreened all 250 names in d9648c4: four shadowed real products (Revalor, Orbaex, Lumemox, Nuvizen) and are removed. They are now a denylist in the spec, alongside a test rejecting INN-stem endings.

allergen() drops Lactose, Fructose and MSG as non-immune per NIAID. Also exported MedicalDefinition (was TS2305) and updated the PR description.

Sonadex is a real brand: paracetamol/caffeine/aspirin tablets from Sydler
Remedies, item 304 of their catalogue, sold at retail in Kenya under ATC
N02BG. It is absent from DailyMed, openFDA, the FDA Green Book and EMA, which
is the same reason Lumemox survived the first screen — those registries cannot
clear a name that is manufactured in India and marketed in East Africa. I
diffed the whole of that catalogue against the list; Sonadex was the only hit.

The denylist listed Orbax, the real product, but not Orbaex, the name actually
removed for shadowing it, so re-adding the generated spelling passed both
tests. Both spellings are now listed.

The stem list omitted the antacid stem, which the WHO stem book writes as
`-ox/-alox`. It is added as `alox` rather than `ox`: WHO assigns no INN on the
bare ending, and 20 of the 38 INNs that end in those letters carry an
unrelated stem, so matching `ox` would flag eleven names that do not carry it.
Twenty further stems are added at the same time — none of them matches any
current name, so the documented guarantee is now checked rather than asserted.

Also corrects Restless Leg Syndrome to Restless Legs Syndrome, the name NINDS
and MedlinePlus use.
rodrigobnogueira added a commit to rodrigobnogueira/faker-healthcare-provider that referenced this pull request Aug 16, 2026
brand_drug() built a name from morphemes on every call: 31,500 reachable
names in the base provider, 30,752 more in the zh_CN Chinese path. It
screened each attempt against 16 WHO INN class stems, retried up to 12
times, and then returned the last attempt anyway if all 12 failed, so a
name ending in an INN stem was reachable in principle ('Zolol' is the one
such name in the space).

The retry loop is not the real problem. Nothing in it screened for real
product names, and 31,500 names cannot be screened for them. When the
same morphemes were ported to faker-js (faker-js/faker#3949), a human
screen of a ~250-name sample found five names shadowing real products --
Revalor, Orbaex, Lumemox, Nuvizen, Sonadex, two of them FDA veterinary
drugs -- and all five were reachable from these pools too. Meanwhile the
README promised the names were "not real trademarks" and that "any
resemblance to a real product is coincidental".

You cannot screen 31,500 names. You can screen 250.

scripts/generate_brand_names.py enumerates the morpheme space with no
RNG, rejects any name that ends in a BRAND_FORBIDDEN_ENDINGS stem, is in
REAL_PRODUCT_DENYLIST, contains a term from OFFENSIVE_SUBSTRINGS (the
check behind the 2.3.1 morpheme fix, which had lived in a throwaway
script), or collides with a drug in any of the six catalogues -- and
writes the reviewed survivors to a generated module, sorted, idempotently.
brand_drug() is now random_element(self.brand_drug_names); the retry loop
and its return-the-last-attempt path are gone.

zh_CN gets the same treatment with a weaker, stated guarantee. Its
Chinese half now draws from ZH_BRAND_NAMES, 64 two-character names
screened against a denylist and against every Chinese term the package
ships. Compiling that denylist by inspection turned up real trademarks in
the reachable set -- Novartis, Tylenol, Taikang, Ping An -- and ordinary
words including the first half of "euthanasia", but it took five passes
and each one found more, so the module carries a TODO(review) saying
plainly that no fluent Chinese speaker has read it.

The guarantee is now executable. tests iterate the whole shipped tuple
rather than sampling the generator: every name passes every screen, the
five known collisions are absent by name, the list is non-empty, sorted
and deduplicated, 1000 draws are all members of it, and re-running the
script reproduces both generated files byte for byte.

The morpheme tuples and BRAND_FORBIDDEN_ENDINGS stay: they are the
script's input and part of the public surface.

README, AGENTS.md and CONTRIBUTING.md drop the unfalsifiable claims for
what is actually true -- curated fictional names, screened against a
documented corpus on a stated date, not a trademark search -- and invite
collision reports. AGENTS.md and CONTRIBUTING.md also gain the general
rule this is an instance of: a user-visible generated identifier must
come from a screened, enumerable, committed set, never from a space too
large to screen, and its safety property must be asserted by iterating
the shipped set.
@ST-DDT

ST-DDT commented Aug 17, 2026

Copy link
Copy Markdown
Member

FYI: This is planned for early in v11.0 / after the dependency PRs. It may take a few days though.

ST-DDT
ST-DDT previously approved these changes Aug 18, 2026
@ST-DDT ST-DDT added m: medical Something is referring to the medical module and removed s: pending triage Pending Triage labels Aug 18, 2026
@ST-DDT

ST-DDT commented Aug 18, 2026

Copy link
Copy Markdown
Member

@rodrigobnogueira This PR was hidden/unreachable yesterday. Do you know what happened?

@rodrigobnogueira

Copy link
Copy Markdown
Contributor Author

@rodrigobnogueira This PR was hidden/unreachable yesterday. Do you know what happened?

Yes, for some reason I got my user suspended from Github yesterday. I opened a ticket and it was restored today.

@Shinigami92 Shinigami92 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice work, the wiring and the generated locale files all look correct. A few things before this can go in.

Drug names

The JSDoc claims the screening covers veterinary and non-US/EU products, but it only catches exact matches. Orbaex was removed for shadowing Orbax while Orbamox stayed, same with Lumemox vs Lumein. These are one letter off a real product, please drop them:

  • Solvadex / Nolvadex
  • Zolvia / Zolvix (veterinary) and Zovia
  • Cetrasen / Cetraben
  • Lumein / Lutein

Then either catch near misses in the screening, or reword the JSDoc to claim only what we check. The INN stem part holds by the way, I ran all 245 names against a larger stem list and found no violations.

Allergens

Gluten needs to go. Celiac is autoimmune and NCGS isn't immune mediated, so it's the same case as the lactose and fructose we already exclude, and Wheat is in the list already as the actual allergy.

Aspirin, Ibuprofen, Codeine, Morphine, Contrast Dye and Sulfites aren't immune mediated either. I'd keep those since every EHR records them, but then the JSDoc sentence about excluding non-immune reactions no longer matches the data.

Duplicate values across methods

  • specialty() and department() both return "Oncology", "Pediatrics" and "Radiology". The department list qualifies everything else ("Cardiology Unit"), so those three should too.
  • condition() and symptom() overlap on "Hearing Loss" and "Depression".
  • procedure() has "Gallbladder Removal" next to "Cholecystectomy", and "Dialysis" next to "Hemodialysis".

Symptoms

"Negative Symptoms" and "Withdrawal Symptoms" are category labels, so the output reads Symptom: Negative Symptoms. "Hypertension" is asymptomatic by definition, and "Hypoalbuminemia" and "Proteinuria" are lab values no patient reports. Also "Color Fading" and "Fading Colors" are both in there.

Overview

The ### Overview block only links drugName() and allergen(). The other modules link all of their methods (book, music, animal, food), which is what builds the method index on the API page. Could you add the remaining six?

@matthewmayer

Copy link
Copy Markdown
Contributor

Overview doesn't necessarily have to mention every method. It should guide you to the common commonly used/important ones.

Nine more drug names read as a real product a letter or two away, which an
exact-match screen cannot see: Solvadex/Nolvadex, Zolnodex/Zoladex,
Zolvia/Zolvix, Cetrasen/Cetraben, Fendaol/Fendall, Lumein/Lutein,
Sonaex/Sonadex, Uvelmox/Velamox, and Orbamox, which shadows Orbax and reads as
an amoxicillin product besides. Sonaex is the sharpest of them: one deletion
from Sonadex, a product this branch had already removed. The spec now
denylists the real products and the shadowing spellings separately, because
listing only one side lets the other back in.

Sixteen further pairs sit within a single edit of each other -- Advaen and
Advazen, Zentacor and Zentavor, and so on. That is a property of recombining
morphemes rather than a collision with anything real, so they stay.

The JSDoc described a screen the suite does not run. It now says the screen is
a manual step and that the tests lock in what it caught, which is what they do.

allergen() drops Gluten: a record files it as an intolerance, and its IgE
counterpart is already listed as Wheat. The mechanism sentence goes with it --
Aspirin, Codeine and Sulfites are pharmacological rather than immune and are
kept on purpose, so a rule about immune mediation was never true of this list.

No value is returned by two different methods any more. Hearing Loss leaves
symptom.ts, where Difficulty Hearing already carries it; Depression stays as
the symptom and condition.ts takes Major Depressive Disorder, matching its own
Anxiety Disorder and Bipolar Disorder. Raynaud's Phenomenon moves to
condition.ts. Oncology, Pediatrics and Radiology become Oncology Unit,
Pediatric Ward and Radiology Department, the idiom the rest of the file uses.

procedure() drops Gallbladder Removal for Cholecystectomy, and bare Dialysis,
which sat beside both Hemodialysis and Peritoneal Dialysis.

symptom() loses 32 entries: umbrella labels that read as categories
(Gastrointestinal Issues, Vision Problems, Negative Symptoms), lab values no
patient reports (Proteinuria, Hypoalbuminemia, Bone Loss), Hypertension and
High Blood Pressure, asymptomatic and already in condition.ts as Essential
Hypertension, and pairs saying the same thing twice (Rapid Heartbeat kept over
Rapid Heart Rate and Rapid Pulse, Rash over Skin Rash).

The module overview points at the methods a reader starts with rather than
naming every category in turn.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: feature Request for new feature m: medical Something is referring to the medical module p: 1-normal Nothing urgent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add medical module

5 participants