9b395f5cc3
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
Trigger Website Rebuild (Docs Updated) / dispatch (push) Has been cancelled
75 lines
3.4 KiB
JavaScript
75 lines
3.4 KiB
JavaScript
// openfda drug-label — FDA-approved drug label search.
|
|
//
|
|
// Endpoint: GET /drug/label.json?search=<lucene>&limit=<n>
|
|
// Default search field is brand_name OR generic_name (Lucene syntax via openfda
|
|
// query DSL). Returns label sections (purpose, warnings, dosage, etc.) and
|
|
// metadata (manufacturer, product_ndc, route, etc.).
|
|
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
import { EmptyResultError } from '@jackwener/opencli/errors';
|
|
import {
|
|
OPENFDA_BASE,
|
|
firstOrNull,
|
|
joinOrNull,
|
|
openfdaFetch,
|
|
requireBoundedInt,
|
|
requireString,
|
|
} from './utils.js';
|
|
|
|
cli({
|
|
site: 'openfda',
|
|
name: 'drug-label',
|
|
access: 'read',
|
|
description: 'Search FDA-approved drug labels (brand or generic name)',
|
|
domain: 'fda.gov',
|
|
strategy: Strategy.PUBLIC,
|
|
browser: false,
|
|
args: [
|
|
{ name: 'query', positional: true, required: true, help: 'Brand or generic drug name (e.g. "aspirin", "lisinopril")' },
|
|
{ name: 'limit', type: 'int', default: 5, help: 'Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)' },
|
|
],
|
|
columns: [
|
|
'rank', 'id', 'brandName', 'genericName', 'manufacturer',
|
|
'productType', 'route', 'productNdc', 'pharmClass',
|
|
'purpose', 'indications', 'warnings', 'dosage', 'effectiveTime',
|
|
],
|
|
func: async (args) => {
|
|
const query = requireString(args.query, 'query');
|
|
const limit = requireBoundedInt(args.limit, 5, 25);
|
|
const brand = `openfda.brand_name:"${query}"`;
|
|
const generic = `openfda.generic_name:"${query}"`;
|
|
// URLSearchParams encodes spaces/operators in ways openFDA's Lucene
|
|
// parser handles poorly. Keep the OR literal visible and encode only
|
|
// each clause, matching food-recall's manual +AND+ handling.
|
|
const search = `${encodeURIComponent(brand)}+OR+${encodeURIComponent(generic)}`;
|
|
const url = `${OPENFDA_BASE}/drug/label.json?search=${search}&limit=${limit}`;
|
|
const body = await openfdaFetch(url, 'openfda drug-label');
|
|
const list = Array.isArray(body?.results) ? body.results : [];
|
|
if (!list.length) {
|
|
throw new EmptyResultError('openfda drug-label', `openFDA returned no labels matching "${query}".`);
|
|
}
|
|
return list.map((r, i) => {
|
|
const o = r?.openfda ?? {};
|
|
// pharm_class fields: epc (established pharmacologic class) is the
|
|
// most user-meaningful — fall back through moa/cs/pe in that order.
|
|
const pharmClass = firstOrNull(o.pharm_class_epc) ?? firstOrNull(o.pharm_class_moa)
|
|
?? firstOrNull(o.pharm_class_cs) ?? firstOrNull(o.pharm_class_pe);
|
|
return {
|
|
rank: i + 1,
|
|
id: r?.id ?? null,
|
|
brandName: firstOrNull(o.brand_name),
|
|
genericName: firstOrNull(o.generic_name),
|
|
manufacturer: firstOrNull(o.manufacturer_name),
|
|
productType: firstOrNull(o.product_type),
|
|
route: joinOrNull(o.route),
|
|
productNdc: firstOrNull(o.product_ndc),
|
|
pharmClass,
|
|
purpose: firstOrNull(r.purpose),
|
|
indications: firstOrNull(r.indications_and_usage),
|
|
warnings: firstOrNull(r.warnings),
|
|
dosage: firstOrNull(r.dosage_and_administration),
|
|
effectiveTime: r?.effective_time ?? null,
|
|
};
|
|
});
|
|
},
|
|
});
|