openfda-mcp-server 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/defineProperty-D36W847_.js +1 -0
- package/dist/handlers/bulk-data-handlers.d.ts +13 -13
- package/dist/handlers/bulk-data-handlers.js +1 -1
- package/dist/handlers/bulk-data-handlers.js.map +1 -1
- package/dist/handlers/device-handlers.d.ts +13 -13
- package/dist/handlers/device-handlers.js +1 -1
- package/dist/handlers/device-handlers.js.map +1 -1
- package/dist/handlers/drug-handlers.d.ts +17 -17
- package/dist/handlers/drug-handlers.js +1 -1
- package/dist/handlers/drug-handlers.js.map +1 -1
- package/dist/handlers/query.d.ts +21 -0
- package/dist/handlers/query.js +2 -0
- package/dist/handlers/query.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +6 -2
- package/dist/lib.js +1 -1
- package/dist/server.d.ts +9 -9
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/index.d.ts +35 -36
- package/dist/tools/index.js.map +1 -1
- package/dist/types/fda.d.ts +19 -23
- package/dist/utils/api-client.d.ts +27 -23
- package/dist/utils/api-client.js +1 -1
- package/dist/utils/api-client.js.map +1 -1
- package/dist/utils/bulk-data-client.d.ts +19 -13
- package/dist/utils/bulk-data-client.js +2 -3
- package/dist/utils/bulk-data-client.js.map +1 -1
- package/dist/utils/errors.d.ts +8 -0
- package/dist/utils/errors.js +2 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/fields.d.ts +11 -0
- package/dist/utils/fields.js +2 -0
- package/dist/utils/fields.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -8
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/version-BNWZ_jnk.d.ts +7 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +16 -12
- package/dist/defineProperty-BEGP2H1W.js +0 -1
package/README.md
CHANGED
|
@@ -72,6 +72,12 @@ Get a free API key at: https://open.fda.gov/apis/authentication/
|
|
|
72
72
|
| `search_device_adverse_events` | Search MDR adverse event reports. Retrieve specific report by `reportNumber`. |
|
|
73
73
|
| `search_device_recalls` | Search device recall enforcement reports |
|
|
74
74
|
|
|
75
|
+
### Built-in Tools
|
|
76
|
+
|
|
77
|
+
| Tool | Description |
|
|
78
|
+
| ------ | --------------------------------------------------------------------------------------- |
|
|
79
|
+
| `info` | Server name, version, build and capability counts. Registered automatically by SomaMCP. |
|
|
80
|
+
|
|
75
81
|
## Tool Parameters
|
|
76
82
|
|
|
77
83
|
### search_drug_labels
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(t){"@babel/helpers - typeof";return e=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},e(t)}function t(t,n){if(e(t)!=`object`||!t)return t;var r;if(typeof Symbol<`u`&&(r=t[Symbol.toPrimitive])!==void 0){var i=r.call(t,n||`default`);if(e(i)!=`object`)return i;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(n===`string`?String:Number)(t)}function n(n){var r=t(n,`string`);return e(r)==`symbol`?r:r+``}function r(e,t,r){return(t=n(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}export{r as t};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk Data Handlers
|
|
3
|
+
* Query handlers for FDA Orange Book and Purple Book data
|
|
4
|
+
*/
|
|
1
5
|
import { FDAToolResponse } from "../types/fda.js";
|
|
2
|
-
|
|
3
|
-
//#region src/handlers/bulk-data-handlers.d.ts
|
|
4
|
-
type OrangeBookSearchParams = {
|
|
6
|
+
export type OrangeBookSearchParams = {
|
|
5
7
|
drugName?: string;
|
|
6
8
|
applicant?: string;
|
|
7
9
|
applNo?: string;
|
|
@@ -25,8 +27,8 @@ type FormattedOrangeBookProduct = {
|
|
|
25
27
|
patentCount: number;
|
|
26
28
|
exclusivityCount: number;
|
|
27
29
|
};
|
|
28
|
-
declare function handleSearchOrangeBook(params: OrangeBookSearchParams): Promise<FDAToolResponse<FormattedOrangeBookProduct[]>>;
|
|
29
|
-
type OrangeBookPatentsParams = {
|
|
30
|
+
export declare function handleSearchOrangeBook(params: OrangeBookSearchParams): Promise<FDAToolResponse<FormattedOrangeBookProduct[]>>;
|
|
31
|
+
export type OrangeBookPatentsParams = {
|
|
30
32
|
drugName?: string;
|
|
31
33
|
applNo?: string;
|
|
32
34
|
patentNo?: string;
|
|
@@ -50,8 +52,8 @@ type FormattedOrangeBookPatent = {
|
|
|
50
52
|
date: string;
|
|
51
53
|
}>;
|
|
52
54
|
};
|
|
53
|
-
declare function handleSearchOrangeBookPatents(params: OrangeBookPatentsParams): Promise<FDAToolResponse<FormattedOrangeBookPatent[]>>;
|
|
54
|
-
type PurpleBookSearchParams = {
|
|
55
|
+
export declare function handleSearchOrangeBookPatents(params: OrangeBookPatentsParams): Promise<FDAToolResponse<FormattedOrangeBookPatent[]>>;
|
|
56
|
+
export type PurpleBookSearchParams = {
|
|
55
57
|
productName?: string;
|
|
56
58
|
applicant?: string;
|
|
57
59
|
blaNumber?: string;
|
|
@@ -78,8 +80,8 @@ type FormattedPurpleBookEntry = {
|
|
|
78
80
|
referenceProductName: string;
|
|
79
81
|
approvalDate: string;
|
|
80
82
|
};
|
|
81
|
-
declare function handleSearchPurpleBook(params: PurpleBookSearchParams): Promise<FDAToolResponse<FormattedPurpleBookEntry[]>>;
|
|
82
|
-
type DrugPatentExpiryParams = {
|
|
83
|
+
export declare function handleSearchPurpleBook(params: PurpleBookSearchParams): Promise<FDAToolResponse<FormattedPurpleBookEntry[]>>;
|
|
84
|
+
export type DrugPatentExpiryParams = {
|
|
83
85
|
drugName?: string;
|
|
84
86
|
applNo?: string;
|
|
85
87
|
includeExclusivity?: boolean;
|
|
@@ -102,7 +104,5 @@ type PatentExpiryEntry = {
|
|
|
102
104
|
date: string;
|
|
103
105
|
}>;
|
|
104
106
|
};
|
|
105
|
-
declare function handleSearchDrugPatentExpiry(params: DrugPatentExpiryParams): Promise<FDAToolResponse<PatentExpiryEntry[]>>;
|
|
106
|
-
|
|
107
|
-
export { DrugPatentExpiryParams, OrangeBookPatentsParams, OrangeBookSearchParams, PurpleBookSearchParams, handleSearchDrugPatentExpiry, handleSearchOrangeBook, handleSearchOrangeBookPatents, handleSearchPurpleBook };
|
|
108
|
-
//# sourceMappingURL=bulk-data-handlers.d.ts.map
|
|
107
|
+
export declare function handleSearchDrugPatentExpiry(params: DrugPatentExpiryParams): Promise<FDAToolResponse<PatentExpiryEntry[]>>;
|
|
108
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{bulkDataClient as e}from"../utils/bulk-data-client.js";import{failure as t}from"../utils/errors.js";import{loggers as n}from"../utils/logger.js";import{Map as r}from"functype";function i(e,t){return!t||e.toLowerCase().includes(t.toLowerCase())}function a(e){return`${e.applType}-${e.applNo}-${e.productNo}`}function o(e,t){return Object.entries(Object.groupBy(e,t)).map(([e,t])=>[e,t??[]])}function s(e,t){return r(o(e,t).map(([e,t])=>[e,t.length]))}function c(e,t){return r(e.map(e=>[t(e),e]))}function l(e,t){return r(o(e,t))}function u(e,t,n){let r=e.length,i=n??0,a=Math.min(t??10,100);return{items:e.slice(i,i+a),total:r}}const d={name:`FDA Orange Book`,lastUpdated:`Monthly`,url:`https://www.fda.gov/drugs/drug-approvals-and-databases/approved-drug-products-therapeutic-equivalence-evaluations-orange-book`},f={name:`FDA Purple Book`,lastUpdated:`Monthly`,url:`https://purplebooksearch.fda.gov/`};async function p(r){return n.tools(`searchOrangeBook`,r),(await e.getOrangeBookData()).fold(t,({products:e,patents:t,exclusivities:n})=>{let o=s(t,a),c=s(n,a),{items:l,total:f}=u(e.filter(e=>!(!i(e.tradeName,r.drugName)&&!i(e.ingredient,r.drugName)||!i(e.applicant,r.applicant)&&!i(e.applicantFullName,r.applicant)||r.applNo&&e.applNo!==r.applNo||!i(e.teCode,r.teCode))),r.limit,r.skip);return{success:!0,data:l.map(e=>{let t=a(e);return{tradeName:e.tradeName,ingredient:e.ingredient,applicant:e.applicant,applicantFullName:e.applicantFullName,applType:e.applType,applNo:e.applNo,productNo:e.productNo,dfRoute:e.dfRoute,teCode:e.teCode,approvalDate:e.approvalDate,rld:e.rld,type:e.type,patentCount:o.getOrElse(t,0),exclusivityCount:c.getOrElse(t,0)}}),totalResults:f,displayedResults:l.length,dataSource:d}})}async function m(r){return n.tools(`searchOrangeBookPatents`,r),(await e.getOrangeBookData()).fold(t,({products:e,patents:t,exclusivities:n})=>{let o=c(e,a),s=l(n,a),{items:f,total:p}=u(t.filter(e=>{if(r.patentNo&&e.patentNo!==r.patentNo||r.applNo&&e.applNo!==r.applNo)return!1;if(r.drugName){let t=o.get(a(e)).orUndefined();if(!t||!i(t.tradeName,r.drugName)&&!i(t.ingredient,r.drugName))return!1}return!0}),r.limit,r.skip);return{success:!0,data:f.map(e=>{let t=a(e),n=o.get(t).orUndefined(),r=s.getOrElse(t,[]);return{patentNo:e.patentNo,patentExpireDate:e.patentExpireDate,drugSubstanceFlag:e.drugSubstanceFlag,drugProductFlag:e.drugProductFlag,patentUseCode:e.patentUseCode,applType:e.applType,applNo:e.applNo,productNo:e.productNo,tradeName:n?.tradeName??``,ingredient:n?.ingredient??``,submissionDate:e.submissionDate,exclusivities:r.map(e=>({code:e.exclusivityCode,date:e.exclusivityDate}))}}),totalResults:p,displayedResults:f.length,dataSource:d}})}async function h(r){return n.tools(`searchPurpleBook`,r),(await e.getPurpleBookData()).fold(t,e=>{let{items:t,total:n}=u(e.filter(e=>!(!i(e.proprietaryName,r.productName)&&!i(e.properName,r.productName)||!i(e.applicant,r.applicant)||r.blaNumber&&e.blaNumber!==r.blaNumber||r.licenseType&&e.blaType!==r.licenseType||r.biosimilar!==void 0&&e.biosimilar!==r.biosimilar||r.interchangeable!==void 0&&e.interchangeable!==r.interchangeable)),r.limit,r.skip);return{success:!0,data:t.map(e=>({blaNumber:e.blaNumber,proprietaryName:e.proprietaryName,properName:e.properName,applicant:e.applicant,blaType:e.blaType,strength:e.strength,dosageForm:e.dosageForm,route:e.route,status:e.status,licensingStatus:e.licensingStatus,biosimilar:e.biosimilar,interchangeable:e.interchangeable,referenceProductBla:e.referenceProductBla,referenceProductName:e.referenceProductProprietaryName,approvalDate:e.approvalDate})),totalResults:n,displayedResults:t.length,dataSource:f}})}async function g(o){n.tools(`searchDrugPatentExpiry`,o);let s=o.includeExclusivity!==!1,f=o.includePurpleBook===!0;return(await e.getOrangeBookData()).fold(e=>Promise.resolve(t(e)),async({products:e,patents:t,exclusivities:n})=>{let p=c(e,a),m=s?l(n,a):r.empty(),h=t.filter(e=>{if(o.applNo&&e.applNo!==o.applNo)return!1;if(o.drugName){let t=p.get(a(e)).orUndefined();if(!t||!i(t.tradeName,o.drugName)&&!i(t.ingredient,o.drugName))return!1}return!0}).map(e=>{let t=a(e),n=p.get(t).orUndefined(),r=m.getOrElse(t,[]);return{source:`Orange Book`,tradeName:n?.tradeName??``,ingredient:n?.ingredient??``,applNo:e.applNo,patentNo:e.patentNo,patentExpireDate:e.patentExpireDate,drugSubstanceFlag:e.drugSubstanceFlag,drugProductFlag:e.drugProductFlag,patentUseCode:e.patentUseCode,exclusivities:r.map(e=>({code:e.exclusivityCode,date:e.exclusivityDate}))}}),g=f?await _(o):[],{items:v,total:y}=u([...h,...g].sort((e,t)=>!e.patentExpireDate&&!t.patentExpireDate?0:e.patentExpireDate?t.patentExpireDate?e.patentExpireDate.localeCompare(t.patentExpireDate):-1:1),o.limit,o.skip);return{success:!0,data:v,totalResults:y,displayedResults:v.length,dataSource:d}})}async function _(t){return(await e.getPurpleBookData()).fold(e=>(n.bulk(`Purple Book data unavailable: ${e.message}`),[]),e=>e.filter(e=>!(t.drugName&&!i(e.proprietaryName,t.drugName)&&!i(e.properName,t.drugName)||t.applNo&&e.blaNumber!==t.applNo)).map(e=>({source:`Purple Book`,tradeName:e.proprietaryName,ingredient:e.properName,applNo:e.blaNumber,patentNo:``,patentExpireDate:``,drugSubstanceFlag:``,drugProductFlag:``,patentUseCode:``,exclusivities:[]})))}export{g as handleSearchDrugPatentExpiry,p as handleSearchOrangeBook,m as handleSearchOrangeBookPatents,h as handleSearchPurpleBook};
|
|
2
2
|
//# sourceMappingURL=bulk-data-handlers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-data-handlers.js","names":[],"sources":["../../src/handlers/bulk-data-handlers.ts"],"sourcesContent":["/**\n * Bulk Data Handlers\n * Query handlers for FDA Orange Book and Purple Book data\n */\n\nimport type { FDAToolResponse, OrangeBookExclusivity, OrangeBookPatent, OrangeBookProduct } from \"../types/fda.js\"\nimport { bulkDataClient } from \"../utils/bulk-data-client.js\"\nimport { loggers } from \"../utils/logger.js\"\n\n// Helper for case-insensitive substring matching\nfunction matchesFilter(value: string, filter: string | undefined): boolean {\n if (!filter) return true\n return value.toLowerCase().includes(filter.toLowerCase())\n}\n\n// Join key for Orange Book records\nfunction obJoinKey(record: { applType: string; applNo: string; productNo: string }): string {\n return `${record.applType}-${record.applNo}-${record.productNo}`\n}\n\n// Default pagination values\nconst DEFAULT_LIMIT = 10\nconst MAX_LIMIT = 100\n\nfunction applyPagination<T>(items: T[], limit?: number, skip?: number): { items: T[]; total: number } {\n const total = items.length\n const effectiveSkip = skip ?? 0\n const effectiveLimit = Math.min(limit ?? DEFAULT_LIMIT, MAX_LIMIT)\n return {\n items: items.slice(effectiveSkip, effectiveSkip + effectiveLimit),\n total,\n }\n}\n\nconst ORANGE_BOOK_SOURCE = {\n name: \"FDA Orange Book\",\n lastUpdated: \"Monthly\",\n url: \"https://www.fda.gov/drugs/drug-approvals-and-databases/approved-drug-products-therapeutic-equivalence-evaluations-orange-book\",\n}\n\nconst PURPLE_BOOK_SOURCE = {\n name: \"FDA Purple Book\",\n lastUpdated: \"Monthly\",\n url: \"https://purplebooksearch.fda.gov/\",\n}\n\n// ============================================================\n// search_fda_orange_book\n// ============================================================\n\nexport type OrangeBookSearchParams = {\n drugName?: string\n applicant?: string\n applNo?: string\n teCode?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedOrangeBookProduct = {\n tradeName: string\n ingredient: string\n applicant: string\n applicantFullName: string\n applType: string\n applNo: string\n productNo: string\n dfRoute: string\n teCode: string\n approvalDate: string\n rld: string\n type: string\n patentCount: number\n exclusivityCount: number\n}\n\nexport async function handleSearchOrangeBook(\n params: OrangeBookSearchParams,\n): Promise<FDAToolResponse<FormattedOrangeBookProduct[]>> {\n loggers.tools(\"searchOrangeBook\", params)\n\n try {\n const { products, patents, exclusivities } = await bulkDataClient.getOrangeBookData()\n\n // Build patent/exclusivity count maps\n const patentCounts = new Map<string, number>()\n for (const p of patents) {\n const key = obJoinKey(p)\n patentCounts.set(key, (patentCounts.get(key) ?? 0) + 1)\n }\n\n const exclusivityCounts = new Map<string, number>()\n for (const e of exclusivities) {\n const key = obJoinKey(e)\n exclusivityCounts.set(key, (exclusivityCounts.get(key) ?? 0) + 1)\n }\n\n // Filter products\n const filtered = products.filter((p) => {\n if (!matchesFilter(p.tradeName, params.drugName) && !matchesFilter(p.ingredient, params.drugName)) return false\n if (!matchesFilter(p.applicant, params.applicant) && !matchesFilter(p.applicantFullName, params.applicant))\n return false\n if (params.applNo && p.applNo !== params.applNo) return false\n if (!matchesFilter(p.teCode, params.teCode)) return false\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedOrangeBookProduct[] = items.map((p) => {\n const key = obJoinKey(p)\n return {\n tradeName: p.tradeName,\n ingredient: p.ingredient,\n applicant: p.applicant,\n applicantFullName: p.applicantFullName,\n applType: p.applType,\n applNo: p.applNo,\n productNo: p.productNo,\n dfRoute: p.dfRoute,\n teCode: p.teCode,\n approvalDate: p.approvalDate,\n rld: p.rld,\n type: p.type,\n patentCount: patentCounts.get(key) ?? 0,\n exclusivityCount: exclusivityCounts.get(key) ?? 0,\n }\n })\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// ============================================================\n// search_fda_orange_book_patents\n// ============================================================\n\nexport type OrangeBookPatentsParams = {\n drugName?: string\n applNo?: string\n patentNo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedOrangeBookPatent = {\n patentNo: string\n patentExpireDate: string\n drugSubstanceFlag: string\n drugProductFlag: string\n patentUseCode: string\n applType: string\n applNo: string\n productNo: string\n tradeName: string\n ingredient: string\n submissionDate: string\n exclusivities: Array<{ code: string; date: string }>\n}\n\nexport async function handleSearchOrangeBookPatents(\n params: OrangeBookPatentsParams,\n): Promise<FDAToolResponse<FormattedOrangeBookPatent[]>> {\n loggers.tools(\"searchOrangeBookPatents\", params)\n\n try {\n const { products, patents, exclusivities } = await bulkDataClient.getOrangeBookData()\n\n // Build product lookup by join key\n const productMap = new Map<string, OrangeBookProduct>()\n for (const p of products) {\n productMap.set(obJoinKey(p), p)\n }\n\n // Build exclusivity lookup by join key\n const exclusivityMap = new Map<string, OrangeBookExclusivity[]>()\n for (const e of exclusivities) {\n const key = obJoinKey(e)\n const existing = exclusivityMap.get(key) ?? []\n existing.push(e)\n exclusivityMap.set(key, existing)\n }\n\n // Filter patents\n const filtered = patents.filter((pat) => {\n if (params.patentNo && pat.patentNo !== params.patentNo) return false\n if (params.applNo && pat.applNo !== params.applNo) return false\n\n if (params.drugName) {\n const product = productMap.get(obJoinKey(pat))\n if (!product) return false\n if (!matchesFilter(product.tradeName, params.drugName) && !matchesFilter(product.ingredient, params.drugName))\n return false\n }\n\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedOrangeBookPatent[] = items.map((pat) => {\n const key = obJoinKey(pat)\n const product = productMap.get(key)\n const excl = exclusivityMap.get(key) ?? []\n\n return {\n patentNo: pat.patentNo,\n patentExpireDate: pat.patentExpireDate,\n drugSubstanceFlag: pat.drugSubstanceFlag,\n drugProductFlag: pat.drugProductFlag,\n patentUseCode: pat.patentUseCode,\n applType: pat.applType,\n applNo: pat.applNo,\n productNo: pat.productNo,\n tradeName: product?.tradeName ?? \"\",\n ingredient: product?.ingredient ?? \"\",\n submissionDate: pat.submissionDate,\n exclusivities: excl.map((e) => ({ code: e.exclusivityCode, date: e.exclusivityDate })),\n }\n })\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// ============================================================\n// search_fda_purple_book\n// ============================================================\n\nexport type PurpleBookSearchParams = {\n productName?: string\n applicant?: string\n blaNumber?: string\n licenseType?: \"351(a)\" | \"351(k)\"\n biosimilar?: boolean\n interchangeable?: boolean\n limit?: number\n skip?: number\n}\n\ntype FormattedPurpleBookEntry = {\n blaNumber: string\n proprietaryName: string\n properName: string\n applicant: string\n blaType: string\n strength: string\n dosageForm: string\n route: string\n status: string\n licensingStatus: string\n biosimilar: boolean\n interchangeable: boolean\n referenceProductBla: string\n referenceProductName: string\n approvalDate: string\n}\n\nexport async function handleSearchPurpleBook(\n params: PurpleBookSearchParams,\n): Promise<FDAToolResponse<FormattedPurpleBookEntry[]>> {\n loggers.tools(\"searchPurpleBook\", params)\n\n try {\n const entries = await bulkDataClient.getPurpleBookData()\n\n const filtered = entries.filter((e) => {\n if (!matchesFilter(e.proprietaryName, params.productName) && !matchesFilter(e.properName, params.productName))\n return false\n if (!matchesFilter(e.applicant, params.applicant)) return false\n if (params.blaNumber && e.blaNumber !== params.blaNumber) return false\n if (params.licenseType && e.blaType !== params.licenseType) return false\n if (params.biosimilar !== undefined && e.biosimilar !== params.biosimilar) return false\n if (params.interchangeable !== undefined && e.interchangeable !== params.interchangeable) return false\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedPurpleBookEntry[] = items.map((e) => ({\n blaNumber: e.blaNumber,\n proprietaryName: e.proprietaryName,\n properName: e.properName,\n applicant: e.applicant,\n blaType: e.blaType,\n strength: e.strength,\n dosageForm: e.dosageForm,\n route: e.route,\n status: e.status,\n licensingStatus: e.licensingStatus,\n biosimilar: e.biosimilar,\n interchangeable: e.interchangeable,\n referenceProductBla: e.referenceProductBla,\n referenceProductName: e.referenceProductProprietaryName,\n approvalDate: e.approvalDate,\n }))\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: PURPLE_BOOK_SOURCE,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// ============================================================\n// search_fda_drug_patent_expiry\n// ============================================================\n\nexport type DrugPatentExpiryParams = {\n drugName?: string\n applNo?: string\n includeExclusivity?: boolean\n includePurpleBook?: boolean\n limit?: number\n skip?: number\n}\n\ntype PatentExpiryEntry = {\n source: string\n tradeName: string\n ingredient: string\n applNo: string\n patentNo: string\n patentExpireDate: string\n drugSubstanceFlag: string\n drugProductFlag: string\n patentUseCode: string\n exclusivities: Array<{ code: string; date: string }>\n}\n\nexport async function handleSearchDrugPatentExpiry(\n params: DrugPatentExpiryParams,\n): Promise<FDAToolResponse<PatentExpiryEntry[]>> {\n loggers.tools(\"searchDrugPatentExpiry\", params)\n\n try {\n const includeExclusivity = params.includeExclusivity !== false\n const includePurpleBook = params.includePurpleBook === true\n\n const { products, patents, exclusivities } = await bulkDataClient.getOrangeBookData()\n\n // Build product lookup\n const productMap = new Map<string, OrangeBookProduct>()\n for (const p of products) {\n productMap.set(obJoinKey(p), p)\n }\n\n // Build exclusivity lookup\n const exclusivityMap = new Map<string, OrangeBookExclusivity[]>()\n if (includeExclusivity) {\n for (const e of exclusivities) {\n const key = obJoinKey(e)\n const existing = exclusivityMap.get(key) ?? []\n existing.push(e)\n exclusivityMap.set(key, existing)\n }\n }\n\n // Filter patents by drug name / applNo\n const filteredPatents = patents.filter((pat) => {\n if (params.applNo && pat.applNo !== params.applNo) return false\n if (params.drugName) {\n const product = productMap.get(obJoinKey(pat))\n if (!product) return false\n if (!matchesFilter(product.tradeName, params.drugName) && !matchesFilter(product.ingredient, params.drugName))\n return false\n }\n return true\n })\n\n const entries: PatentExpiryEntry[] = filteredPatents.map((pat) => {\n const key = obJoinKey(pat)\n const product = productMap.get(key)\n const excl = includeExclusivity ? (exclusivityMap.get(key) ?? []) : []\n\n return {\n source: \"Orange Book\",\n tradeName: product?.tradeName ?? \"\",\n ingredient: product?.ingredient ?? \"\",\n applNo: pat.applNo,\n patentNo: pat.patentNo,\n patentExpireDate: pat.patentExpireDate,\n drugSubstanceFlag: pat.drugSubstanceFlag,\n drugProductFlag: pat.drugProductFlag,\n patentUseCode: pat.patentUseCode,\n exclusivities: excl.map((e) => ({ code: e.exclusivityCode, date: e.exclusivityDate })),\n }\n })\n\n // Optionally add Purple Book entries that match\n if (includePurpleBook) {\n try {\n const purpleEntries = await bulkDataClient.getPurpleBookData()\n const filteredPurple = purpleEntries.filter((e) => {\n if (params.drugName) {\n if (!matchesFilter(e.proprietaryName, params.drugName) && !matchesFilter(e.properName, params.drugName))\n return false\n }\n if (params.applNo && e.blaNumber !== params.applNo) return false\n return true\n })\n\n for (const pe of filteredPurple) {\n entries.push({\n source: \"Purple Book\",\n tradeName: pe.proprietaryName,\n ingredient: pe.properName,\n applNo: pe.blaNumber,\n patentNo: \"\",\n patentExpireDate: \"\",\n drugSubstanceFlag: \"\",\n drugProductFlag: \"\",\n patentUseCode: \"\",\n exclusivities: [],\n })\n }\n } catch (error) {\n loggers.bulk(`Purple Book data unavailable: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n\n // Sort by earliest expiry date (non-empty dates first)\n entries.sort((a, b) => {\n if (!a.patentExpireDate && !b.patentExpireDate) return 0\n if (!a.patentExpireDate) return 1\n if (!b.patentExpireDate) return -1\n return a.patentExpireDate.localeCompare(b.patentExpireDate)\n })\n\n const { items, total } = applyPagination(entries, params.limit, params.skip)\n\n return {\n success: true,\n data: items,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bulk-data-handlers.js","names":["matchesFilter","filter","value","obJoinKey","record","groupEntries","items","keyFn","key","group","buildCountMap","Map","buildLookupMap","item","buildGroupMap","applyPagination","total","effectiveSkip","skip","effectiveLimit","limit","ORANGE_BOOK_SOURCE","PURPLE_BOOK_SOURCE","handleSearchOrangeBook","loggers","params","bulkDataClient","failure","patentCounts","patents","exclusivityCounts","exclusivities","products","p","handleSearchOrangeBookPatents","productMap","exclusivityMap","pat","product","excl","handleSearchPurpleBook","entries","handleSearchDrugPatentExpiry","includeExclusivity","includePurpleBook","error","orangeEntries","purpleEntries","getPurpleBookPatentEntries","a","b","pe"],"sources":["../../src/handlers/bulk-data-handlers.ts"],"sourcesContent":["/**\n * Bulk Data Handlers\n * Query handlers for FDA Orange Book and Purple Book data\n */\n\nimport { Map } from \"functype\"\n\nimport type { FDAToolResponse, OrangeBookExclusivity } from \"../types/fda.js\"\nimport { bulkDataClient } from \"../utils/bulk-data-client.js\"\nimport { failure } from \"../utils/errors.js\"\nimport { loggers } from \"../utils/logger.js\"\n\n// Helper for case-insensitive substring matching\nfunction matchesFilter(value: string, filter: string | undefined): boolean {\n if (!filter) return true\n return value.toLowerCase().includes(filter.toLowerCase())\n}\n\n// Join key for Orange Book records\nfunction obJoinKey(record: { applType: string; applNo: string; productNo: string }): string {\n return `${record.applType}-${record.applNo}-${record.productNo}`\n}\n\n// Group items by key in one linear pass (Object.groupBy). List.groupBy is much\n// slower on Orange Book-sized inputs, and these maps are rebuilt per query.\n// Object.entries lists integer-like keys first, in ascending order; obJoinKey\n// keys are never integer-like, and these maps are only used for lookups.\nfunction groupEntries<T>(items: readonly T[], keyFn: (item: T) => string): [string, T[]][] {\n return Object.entries(Object.groupBy(items, keyFn)).map(([key, group]): [string, T[]] => [key, group ?? []])\n}\n\n// Build a counting map from an array using a key function\nfunction buildCountMap<T>(items: readonly T[], keyFn: (item: T) => string): Map<string, number> {\n return Map(groupEntries(items, keyFn).map(([key, group]): [string, number] => [key, group.length]))\n}\n\n// Build a lookup map from an array using a key function (last item wins on duplicate keys)\nfunction buildLookupMap<T>(items: readonly T[], keyFn: (item: T) => string): Map<string, T> {\n return Map(items.map((item): [string, T] => [keyFn(item), item]))\n}\n\n// Build a grouped map from an array using a key function\nfunction buildGroupMap<T>(items: readonly T[], keyFn: (item: T) => string): Map<string, T[]> {\n return Map(groupEntries(items, keyFn))\n}\n\n// Default pagination values\nconst DEFAULT_LIMIT = 10\nconst MAX_LIMIT = 100\n\nfunction applyPagination<T>(items: T[], limit?: number, skip?: number): { items: T[]; total: number } {\n const total = items.length\n const effectiveSkip = skip ?? 0\n const effectiveLimit = Math.min(limit ?? DEFAULT_LIMIT, MAX_LIMIT)\n return {\n items: items.slice(effectiveSkip, effectiveSkip + effectiveLimit),\n total,\n }\n}\n\nconst ORANGE_BOOK_SOURCE = {\n name: \"FDA Orange Book\",\n lastUpdated: \"Monthly\",\n url: \"https://www.fda.gov/drugs/drug-approvals-and-databases/approved-drug-products-therapeutic-equivalence-evaluations-orange-book\",\n}\n\nconst PURPLE_BOOK_SOURCE = {\n name: \"FDA Purple Book\",\n lastUpdated: \"Monthly\",\n url: \"https://purplebooksearch.fda.gov/\",\n}\n\n// ============================================================\n// search_fda_orange_book\n// ============================================================\n\nexport type OrangeBookSearchParams = {\n drugName?: string\n applicant?: string\n applNo?: string\n teCode?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedOrangeBookProduct = {\n tradeName: string\n ingredient: string\n applicant: string\n applicantFullName: string\n applType: string\n applNo: string\n productNo: string\n dfRoute: string\n teCode: string\n approvalDate: string\n rld: string\n type: string\n patentCount: number\n exclusivityCount: number\n}\n\nexport async function handleSearchOrangeBook(\n params: OrangeBookSearchParams,\n): Promise<FDAToolResponse<FormattedOrangeBookProduct[]>> {\n loggers.tools(\"searchOrangeBook\", params)\n\n return (await bulkDataClient.getOrangeBookData()).fold(\n failure<FormattedOrangeBookProduct[]>,\n ({ products, patents, exclusivities }) => {\n const patentCounts = buildCountMap(patents, obJoinKey)\n const exclusivityCounts = buildCountMap(exclusivities, obJoinKey)\n\n const filtered = products.filter((p) => {\n if (!matchesFilter(p.tradeName, params.drugName) && !matchesFilter(p.ingredient, params.drugName)) return false\n if (!matchesFilter(p.applicant, params.applicant) && !matchesFilter(p.applicantFullName, params.applicant))\n return false\n if (params.applNo && p.applNo !== params.applNo) return false\n if (!matchesFilter(p.teCode, params.teCode)) return false\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedOrangeBookProduct[] = items.map((p) => {\n const key = obJoinKey(p)\n return {\n tradeName: p.tradeName,\n ingredient: p.ingredient,\n applicant: p.applicant,\n applicantFullName: p.applicantFullName,\n applType: p.applType,\n applNo: p.applNo,\n productNo: p.productNo,\n dfRoute: p.dfRoute,\n teCode: p.teCode,\n approvalDate: p.approvalDate,\n rld: p.rld,\n type: p.type,\n patentCount: patentCounts.getOrElse(key, 0),\n exclusivityCount: exclusivityCounts.getOrElse(key, 0),\n }\n })\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n },\n )\n}\n\n// ============================================================\n// search_fda_orange_book_patents\n// ============================================================\n\nexport type OrangeBookPatentsParams = {\n drugName?: string\n applNo?: string\n patentNo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedOrangeBookPatent = {\n patentNo: string\n patentExpireDate: string\n drugSubstanceFlag: string\n drugProductFlag: string\n patentUseCode: string\n applType: string\n applNo: string\n productNo: string\n tradeName: string\n ingredient: string\n submissionDate: string\n exclusivities: Array<{ code: string; date: string }>\n}\n\nexport async function handleSearchOrangeBookPatents(\n params: OrangeBookPatentsParams,\n): Promise<FDAToolResponse<FormattedOrangeBookPatent[]>> {\n loggers.tools(\"searchOrangeBookPatents\", params)\n\n return (await bulkDataClient.getOrangeBookData()).fold(\n failure<FormattedOrangeBookPatent[]>,\n ({ products, patents, exclusivities }) => {\n const productMap = buildLookupMap(products, obJoinKey)\n const exclusivityMap = buildGroupMap(exclusivities, obJoinKey)\n\n const filtered = patents.filter((pat) => {\n if (params.patentNo && pat.patentNo !== params.patentNo) return false\n if (params.applNo && pat.applNo !== params.applNo) return false\n\n if (params.drugName) {\n const product = productMap.get(obJoinKey(pat)).orUndefined()\n if (!product) return false\n if (!matchesFilter(product.tradeName, params.drugName) && !matchesFilter(product.ingredient, params.drugName))\n return false\n }\n\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedOrangeBookPatent[] = items.map((pat) => {\n const key = obJoinKey(pat)\n const product = productMap.get(key).orUndefined()\n const excl = exclusivityMap.getOrElse(key, [])\n\n return {\n patentNo: pat.patentNo,\n patentExpireDate: pat.patentExpireDate,\n drugSubstanceFlag: pat.drugSubstanceFlag,\n drugProductFlag: pat.drugProductFlag,\n patentUseCode: pat.patentUseCode,\n applType: pat.applType,\n applNo: pat.applNo,\n productNo: pat.productNo,\n tradeName: product?.tradeName ?? \"\",\n ingredient: product?.ingredient ?? \"\",\n submissionDate: pat.submissionDate,\n exclusivities: excl.map((e) => ({ code: e.exclusivityCode, date: e.exclusivityDate })),\n }\n })\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n },\n )\n}\n\n// ============================================================\n// search_fda_purple_book\n// ============================================================\n\nexport type PurpleBookSearchParams = {\n productName?: string\n applicant?: string\n blaNumber?: string\n licenseType?: \"351(a)\" | \"351(k)\"\n biosimilar?: boolean\n interchangeable?: boolean\n limit?: number\n skip?: number\n}\n\ntype FormattedPurpleBookEntry = {\n blaNumber: string\n proprietaryName: string\n properName: string\n applicant: string\n blaType: string\n strength: string\n dosageForm: string\n route: string\n status: string\n licensingStatus: string\n biosimilar: boolean\n interchangeable: boolean\n referenceProductBla: string\n referenceProductName: string\n approvalDate: string\n}\n\nexport async function handleSearchPurpleBook(\n params: PurpleBookSearchParams,\n): Promise<FDAToolResponse<FormattedPurpleBookEntry[]>> {\n loggers.tools(\"searchPurpleBook\", params)\n\n return (await bulkDataClient.getPurpleBookData()).fold(failure<FormattedPurpleBookEntry[]>, (entries) => {\n const filtered = entries.filter((e) => {\n if (!matchesFilter(e.proprietaryName, params.productName) && !matchesFilter(e.properName, params.productName))\n return false\n if (!matchesFilter(e.applicant, params.applicant)) return false\n if (params.blaNumber && e.blaNumber !== params.blaNumber) return false\n if (params.licenseType && e.blaType !== params.licenseType) return false\n if (params.biosimilar !== undefined && e.biosimilar !== params.biosimilar) return false\n if (params.interchangeable !== undefined && e.interchangeable !== params.interchangeable) return false\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedPurpleBookEntry[] = items.map((e) => ({\n blaNumber: e.blaNumber,\n proprietaryName: e.proprietaryName,\n properName: e.properName,\n applicant: e.applicant,\n blaType: e.blaType,\n strength: e.strength,\n dosageForm: e.dosageForm,\n route: e.route,\n status: e.status,\n licensingStatus: e.licensingStatus,\n biosimilar: e.biosimilar,\n interchangeable: e.interchangeable,\n referenceProductBla: e.referenceProductBla,\n referenceProductName: e.referenceProductProprietaryName,\n approvalDate: e.approvalDate,\n }))\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: PURPLE_BOOK_SOURCE,\n }\n })\n}\n\n// ============================================================\n// search_fda_drug_patent_expiry\n// ============================================================\n\nexport type DrugPatentExpiryParams = {\n drugName?: string\n applNo?: string\n includeExclusivity?: boolean\n includePurpleBook?: boolean\n limit?: number\n skip?: number\n}\n\ntype PatentExpiryEntry = {\n source: string\n tradeName: string\n ingredient: string\n applNo: string\n patentNo: string\n patentExpireDate: string\n drugSubstanceFlag: string\n drugProductFlag: string\n patentUseCode: string\n exclusivities: Array<{ code: string; date: string }>\n}\n\nexport async function handleSearchDrugPatentExpiry(\n params: DrugPatentExpiryParams,\n): Promise<FDAToolResponse<PatentExpiryEntry[]>> {\n loggers.tools(\"searchDrugPatentExpiry\", params)\n\n const includeExclusivity = params.includeExclusivity !== false\n const includePurpleBook = params.includePurpleBook === true\n\n return (await bulkDataClient.getOrangeBookData()).fold(\n (error) => Promise.resolve(failure<PatentExpiryEntry[]>(error)),\n async ({ products, patents, exclusivities }) => {\n const productMap = buildLookupMap(products, obJoinKey)\n const exclusivityMap = includeExclusivity\n ? buildGroupMap(exclusivities, obJoinKey)\n : Map.empty<string, OrangeBookExclusivity[]>()\n\n // Filter patents by drug name / applNo\n const filteredPatents = patents.filter((pat) => {\n if (params.applNo && pat.applNo !== params.applNo) return false\n if (params.drugName) {\n const product = productMap.get(obJoinKey(pat)).orUndefined()\n if (!product) return false\n if (!matchesFilter(product.tradeName, params.drugName) && !matchesFilter(product.ingredient, params.drugName))\n return false\n }\n return true\n })\n\n const orangeEntries: PatentExpiryEntry[] = filteredPatents.map((pat) => {\n const key = obJoinKey(pat)\n const product = productMap.get(key).orUndefined()\n const excl = exclusivityMap.getOrElse(key, [])\n\n return {\n source: \"Orange Book\",\n tradeName: product?.tradeName ?? \"\",\n ingredient: product?.ingredient ?? \"\",\n applNo: pat.applNo,\n patentNo: pat.patentNo,\n patentExpireDate: pat.patentExpireDate,\n drugSubstanceFlag: pat.drugSubstanceFlag,\n drugProductFlag: pat.drugProductFlag,\n patentUseCode: pat.patentUseCode,\n exclusivities: excl.map((e) => ({ code: e.exclusivityCode, date: e.exclusivityDate })),\n }\n })\n\n // Optionally add Purple Book entries that match\n const purpleEntries: PatentExpiryEntry[] = includePurpleBook ? await getPurpleBookPatentEntries(params) : []\n\n const entries = [...orangeEntries, ...purpleEntries].sort((a, b) => {\n if (!a.patentExpireDate && !b.patentExpireDate) return 0\n if (!a.patentExpireDate) return 1\n if (!b.patentExpireDate) return -1\n return a.patentExpireDate.localeCompare(b.patentExpireDate)\n })\n\n const { items, total } = applyPagination(entries, params.limit, params.skip)\n\n return {\n success: true,\n data: items,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n },\n )\n}\n\nasync function getPurpleBookPatentEntries(params: DrugPatentExpiryParams): Promise<PatentExpiryEntry[]> {\n return (await bulkDataClient.getPurpleBookData()).fold(\n (error) => {\n loggers.bulk(`Purple Book data unavailable: ${error.message}`)\n return []\n },\n (purpleEntries) =>\n purpleEntries\n .filter((e) => {\n if (params.drugName) {\n if (!matchesFilter(e.proprietaryName, params.drugName) && !matchesFilter(e.properName, params.drugName))\n return false\n }\n if (params.applNo && e.blaNumber !== params.applNo) return false\n return true\n })\n .map((pe): PatentExpiryEntry => ({\n source: \"Purple Book\",\n tradeName: pe.proprietaryName,\n ingredient: pe.properName,\n applNo: pe.blaNumber,\n patentNo: \"\",\n patentExpireDate: \"\",\n drugSubstanceFlag: \"\",\n drugProductFlag: \"\",\n patentUseCode: \"\",\n exclusivities: [],\n })),\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Handlers
|
|
3
|
+
* Query handlers for FDA device-related endpoints
|
|
4
|
+
*/
|
|
1
5
|
import { FDAToolResponse } from "../types/fda.js";
|
|
2
|
-
|
|
3
|
-
//#region src/handlers/device-handlers.d.ts
|
|
4
|
-
type Device510KParams = {
|
|
6
|
+
export type Device510KParams = {
|
|
5
7
|
deviceName?: string;
|
|
6
8
|
applicant?: string;
|
|
7
9
|
productCode?: string;
|
|
@@ -25,8 +27,8 @@ type FormattedDevice510K = {
|
|
|
25
27
|
state: string | undefined;
|
|
26
28
|
country: string | undefined;
|
|
27
29
|
};
|
|
28
|
-
declare function handleSearchDevice510K(params: Device510KParams): Promise<FDAToolResponse<FormattedDevice510K[]>>;
|
|
29
|
-
type DeviceClassificationParams = {
|
|
30
|
+
export declare function handleSearchDevice510K(params: Device510KParams): Promise<FDAToolResponse<FormattedDevice510K[]>>;
|
|
31
|
+
export type DeviceClassificationParams = {
|
|
30
32
|
deviceName?: string;
|
|
31
33
|
deviceClass?: "1" | "2" | "3";
|
|
32
34
|
medicalSpecialty?: string;
|
|
@@ -47,8 +49,8 @@ type FormattedDeviceClassification = {
|
|
|
47
49
|
implant: boolean;
|
|
48
50
|
lifeSustaining: boolean;
|
|
49
51
|
};
|
|
50
|
-
declare function handleSearchDeviceClassifications(params: DeviceClassificationParams): Promise<FDAToolResponse<FormattedDeviceClassification[]>>;
|
|
51
|
-
type DeviceAdverseEventsParams = {
|
|
52
|
+
export declare function handleSearchDeviceClassifications(params: DeviceClassificationParams): Promise<FDAToolResponse<FormattedDeviceClassification[]>>;
|
|
53
|
+
export type DeviceAdverseEventsParams = {
|
|
52
54
|
reportNumber?: string;
|
|
53
55
|
deviceName?: string;
|
|
54
56
|
brandName?: string;
|
|
@@ -79,8 +81,8 @@ type FormattedDeviceAdverseEvent = {
|
|
|
79
81
|
text: string | undefined;
|
|
80
82
|
}>;
|
|
81
83
|
};
|
|
82
|
-
declare function handleSearchDeviceAdverseEvents(params: DeviceAdverseEventsParams): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>>;
|
|
83
|
-
type DeviceEnforcementParams = {
|
|
84
|
+
export declare function handleSearchDeviceAdverseEvents(params: DeviceAdverseEventsParams): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>>;
|
|
85
|
+
export type DeviceEnforcementParams = {
|
|
84
86
|
recallingFirm?: string;
|
|
85
87
|
productDescription?: string;
|
|
86
88
|
classification?: "Class I" | "Class II" | "Class III";
|
|
@@ -104,7 +106,5 @@ type FormattedDeviceEnforcement = {
|
|
|
104
106
|
deviceName: string | undefined;
|
|
105
107
|
deviceClass: string | undefined;
|
|
106
108
|
};
|
|
107
|
-
declare function handleSearchDeviceEnforcement(params: DeviceEnforcementParams): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>>;
|
|
108
|
-
|
|
109
|
-
export { Device510KParams, DeviceAdverseEventsParams, DeviceClassificationParams, DeviceEnforcementParams, handleSearchDevice510K, handleSearchDeviceAdverseEvents, handleSearchDeviceClassifications, handleSearchDeviceEnforcement };
|
|
110
|
-
//# sourceMappingURL=device-handlers.d.ts.map
|
|
109
|
+
export declare function handleSearchDeviceEnforcement(params: DeviceEnforcementParams): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>>;
|
|
110
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{fdaAPIClient as e}from"../utils/api-client.js";import{loggers as t}from"../utils/logger.js";import{buildDateQuery as n,buildSearchQuery as r,escapeSearchTerm as i,joinQueries as a,toSearchResponse as o,truncateText as s}from"./query.js";function c(e){return{kNumber:e.k_number,deviceName:e.device_name??e.openfda?.device_name,applicant:e.applicant,productCode:e.product_code,clearanceType:e.clearance_type,decisionCode:e.decision_code,decisionDescription:e.decision_description,decisionDate:e.decision_date,dateReceived:e.date_received,city:e.city,state:e.state,country:e.country_code}}async function l(i){t.tools(`searchDevice510K`,i);let s=[{field:`device_name`,value:i.deviceName},{field:`applicant`,value:i.applicant},{field:`product_code`,value:i.productCode},{field:`clearance_type`,value:i.clearanceType}],l=r(s),u=n(`decision_date`,i.decisionDateFrom,i.decisionDateTo),d={search:a(l,u)||void 0,limit:i.limit,skip:i.skip};return o(await e.searchDevice510K(d),d,c)}function u(e){return{deviceName:e.device_name,deviceClass:e.device_class,definition:s(e.definition).orUndefined(),medicalSpecialty:e.medical_specialty,medicalSpecialtyDescription:e.medical_specialty_description,productCode:e.product_code,regulationNumber:e.regulation_number,gmpExempt:e.gmp_exempt_flag===`Y`,implant:e.implant_flag===`Y`,lifeSustaining:e.life_sustain_support_flag===`Y`}}async function d(n){t.tools(`searchDeviceClassifications`,n);let i=[{field:`device_name`,value:n.deviceName},{field:`device_class`,value:n.deviceClass},{field:`medical_specialty`,value:n.medicalSpecialty},{field:`product_code`,value:n.productCode},{field:`regulation_number`,value:n.regulationNumber}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDeviceClassifications(a),a,u)}function f(e){return{reportNumber:e.report_number,dateOfEvent:e.date_of_event,dateReceived:e.date_received,eventType:e.event_type,adverseEventFlag:e.adverse_event_flag===`Y`,productProblemFlag:e.product_problem_flag===`Y`,devices:e.device?.slice(0,3).map(e=>({brandName:e.brand_name,genericName:e.generic_name,manufacturerName:e.manufacturer_d_name,modelNumber:e.model_number,productCode:e.device_report_product_code,deviceClass:e.openfda?.device_class}))??[],mdrText:e.mdr_text?.slice(0,2).map(e=>({textType:e.text_type_code,text:s(e.text,300).orUndefined()}))??[]}}async function p(s){if(t.tools(`searchDeviceAdverseEvents`,s),s.reportNumber){let t={search:`report_number:"${i(s.reportNumber)}"`,limit:1};return o(await e.searchDeviceAdverseEvents(t),t,f)}let c=[{field:`device.generic_name`,value:s.deviceName},{field:`device.brand_name`,value:s.brandName},{field:`device.manufacturer_d_name`,value:s.manufacturerName},{field:`event_type`,value:s.eventType}],l=r(c),u=n(`date_received`,s.dateFrom,s.dateTo),d={search:a(l,u)||void 0,limit:s.limit,skip:s.skip};return o(await e.searchDeviceAdverseEvents(d),d,f)}function m(e){return{recallNumber:e.recall_number,recallingFirm:e.recalling_firm,classification:e.classification,status:e.status,productDescription:s(e.product_description).orUndefined(),reasonForRecall:s(e.reason_for_recall).orUndefined(),recallInitiationDate:e.recall_initiation_date,distributionPattern:s(e.distribution_pattern).orUndefined(),city:e.city,state:e.state,deviceName:e.openfda?.device_name,deviceClass:e.openfda?.device_class}}async function h(i){t.tools(`searchDeviceEnforcement`,i);let s=[{field:`recalling_firm`,value:i.recallingFirm},{field:`product_description`,value:i.productDescription},{field:`classification`,value:i.classification},{field:`status`,value:i.status}],c=r(s),l=n(`recall_initiation_date`,i.dateFrom,i.dateTo),u={search:a(c,l)||void 0,limit:i.limit,skip:i.skip};return o(await e.searchDeviceEnforcement(u),u,m)}export{l as handleSearchDevice510K,p as handleSearchDeviceAdverseEvents,d as handleSearchDeviceClassifications,h as handleSearchDeviceEnforcement};
|
|
2
2
|
//# sourceMappingURL=device-handlers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-handlers.js","names":[],"sources":["../../src/handlers/device-handlers.ts"],"sourcesContent":["/**\n * Device Handlers\n * Query handlers for FDA device-related endpoints\n */\n\nimport type {\n Device510K,\n DeviceAdverseEvent,\n DeviceClassification,\n DeviceEnforcement,\n FDAToolResponse,\n SearchParams,\n} from \"../types/fda.js\"\nimport { fdaAPIClient } from \"../utils/api-client.js\"\nimport { loggers } from \"../utils/logger.js\"\n\n// Helper to build date range query\nfunction buildDateQuery(field: string, dateFrom?: string, dateTo?: string): string | undefined {\n if (!dateFrom && !dateTo) return undefined\n\n // FDA date format is YYYYMMDD\n const from = dateFrom?.replace(/-/g, \"\") ?? \"*\"\n const to = dateTo?.replace(/-/g, \"\") ?? \"*\"\n\n return `${field}:[${from}+TO+${to}]`\n}\n\n// Helper to escape special characters in search terms\nfunction escapeSearchTerm(term: string): string {\n return term.replace(/[+\\-&|!(){}[\\]^\"~*?:\\\\]/g, \"\\\\$&\").replace(/\\s+/g, \"+\")\n}\n\n// Helper to build a search query from multiple terms\nfunction buildSearchQuery(terms: Array<{ field: string; value?: string }>): string {\n const validTerms = terms.filter((t) => t.value !== undefined && t.value.trim() !== \"\")\n if (validTerms.length === 0) return \"\"\n\n return validTerms.map((t) => `${t.field}:\"${escapeSearchTerm(t.value!)}\"`).join(\"+AND+\")\n}\n\n// Format truncated text\nfunction truncateText(text: string | undefined, maxLength = 200): string | undefined {\n if (!text) return undefined\n return text.length > maxLength ? text.substring(0, maxLength) + \"...\" : text\n}\n\n// Device 510(k) Handler\nexport type Device510KParams = {\n deviceName?: string\n applicant?: string\n productCode?: string\n clearanceType?: string\n decisionDateFrom?: string\n decisionDateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDevice510K = {\n kNumber: string | undefined\n deviceName: string | undefined\n applicant: string | undefined\n productCode: string | undefined\n clearanceType: string | undefined\n decisionCode: string | undefined\n decisionDescription: string | undefined\n decisionDate: string | undefined\n dateReceived: string | undefined\n city: string | undefined\n state: string | undefined\n country: string | undefined\n}\n\nfunction formatDevice510K(device: Device510K): FormattedDevice510K {\n return {\n kNumber: device.k_number,\n deviceName: device.device_name ?? device.openfda?.device_name,\n applicant: device.applicant,\n productCode: device.product_code,\n clearanceType: device.clearance_type,\n decisionCode: device.decision_code,\n decisionDescription: device.decision_description,\n decisionDate: device.decision_date,\n dateReceived: device.date_received,\n city: device.city,\n state: device.state,\n country: device.country_code,\n }\n}\n\nexport async function handleSearchDevice510K(\n params: Device510KParams,\n): Promise<FDAToolResponse<FormattedDevice510K[]>> {\n loggers.tools(\"searchDevice510K\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device_name\", value: params.deviceName },\n { field: \"applicant\", value: params.applicant },\n { field: \"product_code\", value: params.productCode },\n { field: \"clearance_type\", value: params.clearanceType },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"decision_date\", params.decisionDateFrom, params.decisionDateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDevice510K(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDevice510K),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Device Classification Handler\nexport type DeviceClassificationParams = {\n deviceName?: string\n deviceClass?: \"1\" | \"2\" | \"3\"\n medicalSpecialty?: string\n productCode?: string\n regulationNumber?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceClassification = {\n deviceName: string | undefined\n deviceClass: string | undefined\n definition: string | undefined\n medicalSpecialty: string | undefined\n medicalSpecialtyDescription: string | undefined\n productCode: string | undefined\n regulationNumber: string | undefined\n gmpExempt: boolean\n implant: boolean\n lifeSustaining: boolean\n}\n\nfunction formatDeviceClassification(classification: DeviceClassification): FormattedDeviceClassification {\n return {\n deviceName: classification.device_name,\n deviceClass: classification.device_class,\n definition: truncateText(classification.definition),\n medicalSpecialty: classification.medical_specialty,\n medicalSpecialtyDescription: classification.medical_specialty_description,\n productCode: classification.product_code,\n regulationNumber: classification.regulation_number,\n gmpExempt: classification.gmp_exempt_flag === \"Y\",\n implant: classification.implant_flag === \"Y\",\n lifeSustaining: classification.life_sustain_support_flag === \"Y\",\n }\n}\n\nexport async function handleSearchDeviceClassifications(\n params: DeviceClassificationParams,\n): Promise<FDAToolResponse<FormattedDeviceClassification[]>> {\n loggers.tools(\"searchDeviceClassifications\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device_name\", value: params.deviceName },\n { field: \"device_class\", value: params.deviceClass },\n { field: \"medical_specialty\", value: params.medicalSpecialty },\n { field: \"product_code\", value: params.productCode },\n { field: \"regulation_number\", value: params.regulationNumber },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n\n const searchParams: SearchParams = {\n search: searchQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDeviceClassifications(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDeviceClassification),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Device Adverse Events (MDR) Handler\nexport type DeviceAdverseEventsParams = {\n reportNumber?: string\n deviceName?: string\n brandName?: string\n manufacturerName?: string\n eventType?: \"Injury\" | \"Malfunction\" | \"Death\" | \"Other\"\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceAdverseEvent = {\n reportNumber: string | undefined\n dateOfEvent: string | undefined\n dateReceived: string | undefined\n eventType: string | undefined\n adverseEventFlag: boolean\n productProblemFlag: boolean\n devices: Array<{\n brandName: string | undefined\n genericName: string | undefined\n manufacturerName: string | undefined\n modelNumber: string | undefined\n productCode: string | undefined\n deviceClass: string | undefined\n }>\n mdrText: Array<{\n textType: string | undefined\n text: string | undefined\n }>\n}\n\nfunction formatDeviceAdverseEvent(event: DeviceAdverseEvent): FormattedDeviceAdverseEvent {\n return {\n reportNumber: event.report_number,\n dateOfEvent: event.date_of_event,\n dateReceived: event.date_received,\n eventType: event.event_type,\n adverseEventFlag: event.adverse_event_flag === \"Y\",\n productProblemFlag: event.product_problem_flag === \"Y\",\n devices:\n event.device?.slice(0, 3).map((d) => ({\n brandName: d.brand_name,\n genericName: d.generic_name,\n manufacturerName: d.manufacturer_d_name,\n modelNumber: d.model_number,\n productCode: d.device_report_product_code,\n deviceClass: d.openfda?.device_class,\n })) ?? [],\n mdrText:\n event.mdr_text?.slice(0, 2).map((t) => ({\n textType: t.text_type_code,\n text: truncateText(t.text, 300),\n })) ?? [],\n }\n}\n\nexport async function handleSearchDeviceAdverseEvents(\n params: DeviceAdverseEventsParams,\n): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>> {\n loggers.tools(\"searchDeviceAdverseEvents\", params)\n\n try {\n // If reportNumber is provided, do a direct lookup\n if (params.reportNumber) {\n const searchParams: SearchParams = {\n search: `report_number:\"${escapeSearchTerm(params.reportNumber)}\"`,\n limit: 1,\n }\n\n const response = await fdaAPIClient.searchDeviceAdverseEvents(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDeviceAdverseEvent),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n }\n\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device.generic_name\", value: params.deviceName },\n { field: \"device.brand_name\", value: params.brandName },\n { field: \"device.manufacturer_d_name\", value: params.manufacturerName },\n { field: \"event_type\", value: params.eventType },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"date_received\", params.dateFrom, params.dateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDeviceAdverseEvents(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDeviceAdverseEvent),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Device Enforcement (Recalls) Handler\nexport type DeviceEnforcementParams = {\n recallingFirm?: string\n productDescription?: string\n classification?: \"Class I\" | \"Class II\" | \"Class III\"\n status?: \"Ongoing\" | \"Completed\" | \"Terminated\" | \"Pending\"\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceEnforcement = {\n recallNumber: string | undefined\n recallingFirm: string | undefined\n classification: string | undefined\n status: string | undefined\n productDescription: string | undefined\n reasonForRecall: string | undefined\n recallInitiationDate: string | undefined\n distributionPattern: string | undefined\n city: string | undefined\n state: string | undefined\n deviceName: string | undefined\n deviceClass: string | undefined\n}\n\nfunction formatDeviceEnforcement(recall: DeviceEnforcement): FormattedDeviceEnforcement {\n return {\n recallNumber: recall.recall_number,\n recallingFirm: recall.recalling_firm,\n classification: recall.classification,\n status: recall.status,\n productDescription: truncateText(recall.product_description),\n reasonForRecall: truncateText(recall.reason_for_recall),\n recallInitiationDate: recall.recall_initiation_date,\n distributionPattern: truncateText(recall.distribution_pattern),\n city: recall.city,\n state: recall.state,\n deviceName: recall.openfda?.device_name,\n deviceClass: recall.openfda?.device_class,\n }\n}\n\nexport async function handleSearchDeviceEnforcement(\n params: DeviceEnforcementParams,\n): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>> {\n loggers.tools(\"searchDeviceEnforcement\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"recalling_firm\", value: params.recallingFirm },\n { field: \"product_description\", value: params.productDescription },\n { field: \"classification\", value: params.classification },\n { field: \"status\", value: params.status },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"recall_initiation_date\", params.dateFrom, params.dateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDeviceEnforcement(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDeviceEnforcement),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"device-handlers.js","names":["formatDevice510K","device","handleSearchDevice510K","loggers","params","searchTerms","searchQuery","buildSearchQuery","dateQuery","buildDateQuery","searchParams","joinQueries","toSearchResponse","fdaAPIClient","formatDeviceClassification","classification","truncateText","handleSearchDeviceClassifications","formatDeviceAdverseEvent","event","d","t","handleSearchDeviceAdverseEvents","escapeSearchTerm","formatDeviceEnforcement","recall","handleSearchDeviceEnforcement"],"sources":["../../src/handlers/device-handlers.ts"],"sourcesContent":["/**\n * Device Handlers\n * Query handlers for FDA device-related endpoints\n */\n\nimport type {\n Device510K,\n DeviceAdverseEvent,\n DeviceClassification,\n DeviceEnforcement,\n FDAToolResponse,\n SearchParams,\n} from \"../types/fda.js\"\nimport { fdaAPIClient } from \"../utils/api-client.js\"\nimport { loggers } from \"../utils/logger.js\"\nimport {\n buildDateQuery,\n buildSearchQuery,\n escapeSearchTerm,\n joinQueries,\n toSearchResponse,\n truncateText,\n} from \"./query.js\"\n\n// Device 510(k) Handler\nexport type Device510KParams = {\n deviceName?: string\n applicant?: string\n productCode?: string\n clearanceType?: string\n decisionDateFrom?: string\n decisionDateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDevice510K = {\n kNumber: string | undefined\n deviceName: string | undefined\n applicant: string | undefined\n productCode: string | undefined\n clearanceType: string | undefined\n decisionCode: string | undefined\n decisionDescription: string | undefined\n decisionDate: string | undefined\n dateReceived: string | undefined\n city: string | undefined\n state: string | undefined\n country: string | undefined\n}\n\nfunction formatDevice510K(device: Device510K): FormattedDevice510K {\n return {\n kNumber: device.k_number,\n deviceName: device.device_name ?? device.openfda?.device_name,\n applicant: device.applicant,\n productCode: device.product_code,\n clearanceType: device.clearance_type,\n decisionCode: device.decision_code,\n decisionDescription: device.decision_description,\n decisionDate: device.decision_date,\n dateReceived: device.date_received,\n city: device.city,\n state: device.state,\n country: device.country_code,\n }\n}\n\nexport async function handleSearchDevice510K(\n params: Device510KParams,\n): Promise<FDAToolResponse<FormattedDevice510K[]>> {\n loggers.tools(\"searchDevice510K\", params)\n\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device_name\", value: params.deviceName },\n { field: \"applicant\", value: params.applicant },\n { field: \"product_code\", value: params.productCode },\n { field: \"clearance_type\", value: params.clearanceType },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"decision_date\", params.decisionDateFrom, params.decisionDateTo)\n const fullQuery = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(await fdaAPIClient.searchDevice510K(searchParams), searchParams, formatDevice510K)\n}\n\n// Device Classification Handler\nexport type DeviceClassificationParams = {\n deviceName?: string\n deviceClass?: \"1\" | \"2\" | \"3\"\n medicalSpecialty?: string\n productCode?: string\n regulationNumber?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceClassification = {\n deviceName: string | undefined\n deviceClass: string | undefined\n definition: string | undefined\n medicalSpecialty: string | undefined\n medicalSpecialtyDescription: string | undefined\n productCode: string | undefined\n regulationNumber: string | undefined\n gmpExempt: boolean\n implant: boolean\n lifeSustaining: boolean\n}\n\nfunction formatDeviceClassification(classification: DeviceClassification): FormattedDeviceClassification {\n return {\n deviceName: classification.device_name,\n deviceClass: classification.device_class,\n definition: truncateText(classification.definition).orUndefined(),\n medicalSpecialty: classification.medical_specialty,\n medicalSpecialtyDescription: classification.medical_specialty_description,\n productCode: classification.product_code,\n regulationNumber: classification.regulation_number,\n gmpExempt: classification.gmp_exempt_flag === \"Y\",\n implant: classification.implant_flag === \"Y\",\n lifeSustaining: classification.life_sustain_support_flag === \"Y\",\n }\n}\n\nexport async function handleSearchDeviceClassifications(\n params: DeviceClassificationParams,\n): Promise<FDAToolResponse<FormattedDeviceClassification[]>> {\n loggers.tools(\"searchDeviceClassifications\", params)\n\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device_name\", value: params.deviceName },\n { field: \"device_class\", value: params.deviceClass },\n { field: \"medical_specialty\", value: params.medicalSpecialty },\n { field: \"product_code\", value: params.productCode },\n { field: \"regulation_number\", value: params.regulationNumber },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n\n const searchParams: SearchParams = {\n search: searchQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDeviceClassifications(searchParams),\n searchParams,\n formatDeviceClassification,\n )\n}\n\n// Device Adverse Events (MDR) Handler\nexport type DeviceAdverseEventsParams = {\n reportNumber?: string\n deviceName?: string\n brandName?: string\n manufacturerName?: string\n eventType?: \"Injury\" | \"Malfunction\" | \"Death\" | \"Other\"\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceAdverseEvent = {\n reportNumber: string | undefined\n dateOfEvent: string | undefined\n dateReceived: string | undefined\n eventType: string | undefined\n adverseEventFlag: boolean\n productProblemFlag: boolean\n devices: Array<{\n brandName: string | undefined\n genericName: string | undefined\n manufacturerName: string | undefined\n modelNumber: string | undefined\n productCode: string | undefined\n deviceClass: string | undefined\n }>\n mdrText: Array<{\n textType: string | undefined\n text: string | undefined\n }>\n}\n\nfunction formatDeviceAdverseEvent(event: DeviceAdverseEvent): FormattedDeviceAdverseEvent {\n return {\n reportNumber: event.report_number,\n dateOfEvent: event.date_of_event,\n dateReceived: event.date_received,\n eventType: event.event_type,\n adverseEventFlag: event.adverse_event_flag === \"Y\",\n productProblemFlag: event.product_problem_flag === \"Y\",\n devices:\n event.device?.slice(0, 3).map((d) => ({\n brandName: d.brand_name,\n genericName: d.generic_name,\n manufacturerName: d.manufacturer_d_name,\n modelNumber: d.model_number,\n productCode: d.device_report_product_code,\n deviceClass: d.openfda?.device_class,\n })) ?? [],\n mdrText:\n event.mdr_text?.slice(0, 2).map((t) => ({\n textType: t.text_type_code,\n text: truncateText(t.text, 300).orUndefined(),\n })) ?? [],\n }\n}\n\nexport async function handleSearchDeviceAdverseEvents(\n params: DeviceAdverseEventsParams,\n): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>> {\n loggers.tools(\"searchDeviceAdverseEvents\", params)\n\n // If reportNumber is provided, do a direct lookup\n if (params.reportNumber) {\n const searchParams: SearchParams = {\n search: `report_number:\"${escapeSearchTerm(params.reportNumber)}\"`,\n limit: 1,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDeviceAdverseEvents(searchParams),\n searchParams,\n formatDeviceAdverseEvent,\n )\n }\n\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device.generic_name\", value: params.deviceName },\n { field: \"device.brand_name\", value: params.brandName },\n { field: \"device.manufacturer_d_name\", value: params.manufacturerName },\n { field: \"event_type\", value: params.eventType },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"date_received\", params.dateFrom, params.dateTo)\n const fullQuery = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDeviceAdverseEvents(searchParams),\n searchParams,\n formatDeviceAdverseEvent,\n )\n}\n\n// Device Enforcement (Recalls) Handler\nexport type DeviceEnforcementParams = {\n recallingFirm?: string\n productDescription?: string\n classification?: \"Class I\" | \"Class II\" | \"Class III\"\n status?: \"Ongoing\" | \"Completed\" | \"Terminated\" | \"Pending\"\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceEnforcement = {\n recallNumber: string | undefined\n recallingFirm: string | undefined\n classification: string | undefined\n status: string | undefined\n productDescription: string | undefined\n reasonForRecall: string | undefined\n recallInitiationDate: string | undefined\n distributionPattern: string | undefined\n city: string | undefined\n state: string | undefined\n deviceName: string | undefined\n deviceClass: string | undefined\n}\n\nfunction formatDeviceEnforcement(recall: DeviceEnforcement): FormattedDeviceEnforcement {\n return {\n recallNumber: recall.recall_number,\n recallingFirm: recall.recalling_firm,\n classification: recall.classification,\n status: recall.status,\n productDescription: truncateText(recall.product_description).orUndefined(),\n reasonForRecall: truncateText(recall.reason_for_recall).orUndefined(),\n recallInitiationDate: recall.recall_initiation_date,\n distributionPattern: truncateText(recall.distribution_pattern).orUndefined(),\n city: recall.city,\n state: recall.state,\n deviceName: recall.openfda?.device_name,\n deviceClass: recall.openfda?.device_class,\n }\n}\n\nexport async function handleSearchDeviceEnforcement(\n params: DeviceEnforcementParams,\n): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>> {\n loggers.tools(\"searchDeviceEnforcement\", params)\n\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"recalling_firm\", value: params.recallingFirm },\n { field: \"product_description\", value: params.productDescription },\n { field: \"classification\", value: params.classification },\n { field: \"status\", value: params.status },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"recall_initiation_date\", params.dateFrom, params.dateTo)\n const fullQuery = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDeviceEnforcement(searchParams),\n searchParams,\n formatDeviceEnforcement,\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drug Handlers
|
|
3
|
+
* Query handlers for FDA drug-related endpoints
|
|
4
|
+
*/
|
|
1
5
|
import { FDAToolResponse } from "../types/fda.js";
|
|
2
|
-
|
|
3
|
-
//#region src/handlers/drug-handlers.d.ts
|
|
4
|
-
type DrugAdverseEventsParams = {
|
|
6
|
+
export type DrugAdverseEventsParams = {
|
|
5
7
|
safetyReportId?: string;
|
|
6
8
|
drugName?: string;
|
|
7
9
|
reaction?: string;
|
|
@@ -31,8 +33,8 @@ type FormattedDrugAdverseEvent = {
|
|
|
31
33
|
weight: string | undefined;
|
|
32
34
|
};
|
|
33
35
|
};
|
|
34
|
-
declare function handleSearchDrugAdverseEvents(params: DrugAdverseEventsParams): Promise<FDAToolResponse<FormattedDrugAdverseEvent[]>>;
|
|
35
|
-
type DrugLabelsParams = {
|
|
36
|
+
export declare function handleSearchDrugAdverseEvents(params: DrugAdverseEventsParams): Promise<FDAToolResponse<FormattedDrugAdverseEvent[]>>;
|
|
37
|
+
export type DrugLabelsParams = {
|
|
36
38
|
setId?: string;
|
|
37
39
|
drugName?: string;
|
|
38
40
|
indication?: string;
|
|
@@ -65,8 +67,8 @@ type FormattedDrugLabel = {
|
|
|
65
67
|
storageAndHandling: string | undefined;
|
|
66
68
|
route: string[];
|
|
67
69
|
};
|
|
68
|
-
declare function handleSearchDrugLabels(params: DrugLabelsParams): Promise<FDAToolResponse<FormattedDrugLabel[]>>;
|
|
69
|
-
type DrugNDCParams = {
|
|
70
|
+
export declare function handleSearchDrugLabels(params: DrugLabelsParams): Promise<FDAToolResponse<FormattedDrugLabel[]>>;
|
|
71
|
+
export type DrugNDCParams = {
|
|
70
72
|
productNdc?: string;
|
|
71
73
|
brandName?: string;
|
|
72
74
|
genericName?: string;
|
|
@@ -90,8 +92,8 @@ type FormattedDrugNDC = {
|
|
|
90
92
|
marketingStartDate: string | undefined;
|
|
91
93
|
productType: string | undefined;
|
|
92
94
|
};
|
|
93
|
-
declare function handleSearchDrugNDC(params: DrugNDCParams): Promise<FDAToolResponse<FormattedDrugNDC[]>>;
|
|
94
|
-
type DrugEnforcementParams = {
|
|
95
|
+
export declare function handleSearchDrugNDC(params: DrugNDCParams): Promise<FDAToolResponse<FormattedDrugNDC[]>>;
|
|
96
|
+
export type DrugEnforcementParams = {
|
|
95
97
|
recallingFirm?: string;
|
|
96
98
|
classification?: "Class I" | "Class II" | "Class III";
|
|
97
99
|
status?: "Ongoing" | "Completed" | "Terminated" | "Pending";
|
|
@@ -113,8 +115,8 @@ type FormattedDrugEnforcement = {
|
|
|
113
115
|
city: string | undefined;
|
|
114
116
|
state: string | undefined;
|
|
115
117
|
};
|
|
116
|
-
declare function handleSearchDrugEnforcement(params: DrugEnforcementParams): Promise<FDAToolResponse<FormattedDrugEnforcement[]>>;
|
|
117
|
-
type DrugsFDAParams = {
|
|
118
|
+
export declare function handleSearchDrugEnforcement(params: DrugEnforcementParams): Promise<FDAToolResponse<FormattedDrugEnforcement[]>>;
|
|
119
|
+
export type DrugsFDAParams = {
|
|
118
120
|
sponsorName?: string;
|
|
119
121
|
applicationNumber?: string;
|
|
120
122
|
brandName?: string;
|
|
@@ -141,8 +143,8 @@ type FormattedDrugsFDA = {
|
|
|
141
143
|
statusDate: string | undefined;
|
|
142
144
|
} | undefined;
|
|
143
145
|
};
|
|
144
|
-
declare function handleSearchDrugsFDA(params: DrugsFDAParams): Promise<FDAToolResponse<FormattedDrugsFDA[]>>;
|
|
145
|
-
type DrugShortagesParams = {
|
|
146
|
+
export declare function handleSearchDrugsFDA(params: DrugsFDAParams): Promise<FDAToolResponse<FormattedDrugsFDA[]>>;
|
|
147
|
+
export type DrugShortagesParams = {
|
|
146
148
|
genericName?: string;
|
|
147
149
|
status?: "Current" | "Resolved";
|
|
148
150
|
limit?: number;
|
|
@@ -156,7 +158,5 @@ type FormattedDrugShortage = {
|
|
|
156
158
|
initialPostingDate: string | undefined;
|
|
157
159
|
resolvedShortageDate: string | undefined;
|
|
158
160
|
};
|
|
159
|
-
declare function handleSearchDrugShortages(params: DrugShortagesParams): Promise<FDAToolResponse<FormattedDrugShortage[]>>;
|
|
160
|
-
|
|
161
|
-
export { DrugAdverseEventsParams, DrugEnforcementParams, DrugLabelsParams, DrugNDCParams, DrugShortagesParams, DrugsFDAParams, handleSearchDrugAdverseEvents, handleSearchDrugEnforcement, handleSearchDrugLabels, handleSearchDrugNDC, handleSearchDrugShortages, handleSearchDrugsFDA };
|
|
162
|
-
//# sourceMappingURL=drug-handlers.d.ts.map
|
|
161
|
+
export declare function handleSearchDrugShortages(params: DrugShortagesParams): Promise<FDAToolResponse<FormattedDrugShortage[]>>;
|
|
162
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{fdaAPIClient as e}from"../utils/api-client.js";import{loggers as t}from"../utils/logger.js";import{buildDateQuery as n,buildSearchQuery as r,escapeSearchTerm as i,joinQueries as a,toSearchResponse as o,truncateText as s}from"./query.js";import{None as c,Some as l}from"functype";function u(e){return{reportId:e.safetyreportid,receiveDate:e.receivedate,serious:e.serious===`1`,drugs:e.patient?.drug?.slice(0,3).map(e=>({name:e.medicinalproduct??e.activesubstance?.activesubstancename,indication:e.drugindication,route:e.drugadministrationroute}))??[],reactions:e.patient?.reaction?.slice(0,3).map(e=>({reaction:e.reactionmeddrapt,outcome:e.reactionoutcome}))??[],patient:{age:e.patient?.patientonsetage?`${e.patient.patientonsetage} ${e.patient.patientonsetageunit??``}`:void 0,sex:e.patient?.patientsex===`1`?`Male`:e.patient?.patientsex===`2`?`Female`:void 0,weight:e.patient?.patientweight?`${e.patient.patientweight} kg`:void 0}}}async function d(s){if(t.tools(`searchDrugAdverseEvents`,s),s.safetyReportId){let t={search:`safetyreportid:"${i(s.safetyReportId)}"`,limit:1};return o(await e.searchDrugAdverseEvents(t),t,u)}let c=[{field:`patient.drug.medicinalproduct`,value:s.drugName},{field:`patient.reaction.reactionmeddrapt`,value:s.reaction},{field:`patient.drug.openfda.manufacturer_name`,value:s.manufacturer}];s.serious!==void 0&&c.push({field:`serious`,value:s.serious?`1`:`2`});let l=r(c),d=n(`receivedate`,s.dateFrom,s.dateTo),f={search:a(l,d)||void 0,limit:s.limit,skip:s.skip};return o(await e.searchDrugAdverseEvents(f),f,u)}function f(e,t){let n=e=>!t||t.includes(e);return{setId:e.set_id,brandName:e.openfda?.brand_name?.[0],genericName:e.openfda?.generic_name?.[0],manufacturer:e.openfda?.manufacturer_name?.[0],activeIngredients:e.openfda?.substance_name?.slice(0,5)??[],boxedWarning:n(`boxed_warning`)?s(e.boxed_warning?.[0],500).orUndefined():void 0,indications:n(`indications_and_usage`)?s(e.indications_and_usage?.[0]).orUndefined():void 0,warnings:n(`warnings`)?s(e.warnings?.[0]).orUndefined():void 0,contraindications:n(`contraindications`)?s(e.contraindications?.[0]).orUndefined():void 0,adverseReactions:n(`adverse_reactions`)?s(e.adverse_reactions?.[0]).orUndefined():void 0,drugInteractions:n(`drug_interactions`)?s(e.drug_interactions?.[0]).orUndefined():void 0,dosageAndAdministration:n(`dosage_and_administration`)?s(e.dosage_and_administration?.[0]).orUndefined():void 0,clinicalPharmacology:n(`clinical_pharmacology`)?s(e.clinical_pharmacology?.[0]).orUndefined():void 0,mechanismOfAction:n(`mechanism_of_action`)?s(e.mechanism_of_action?.[0]).orUndefined():void 0,pharmacokinetics:n(`pharmacokinetics`)?s(e.pharmacokinetics?.[0]).orUndefined():void 0,overdosage:n(`overdosage`)?s(e.overdosage?.[0]).orUndefined():void 0,description:n(`description`)?s(e.description?.[0]).orUndefined():void 0,howSupplied:n(`how_supplied`)?s(e.how_supplied?.[0]).orUndefined():void 0,storageAndHandling:n(`storage_and_handling`)?s(e.storage_and_handling?.[0]).orUndefined():void 0,route:e.openfda?.route?.slice(0,3)??[]}}async function p(n){if(t.tools(`searchDrugLabels`,n),n.setId){let t={search:`set_id:"${i(n.setId)}"`,limit:1};return o(await e.searchDrugLabels(t),t,e=>f(e,n.sections))}let s=[{field:`openfda.brand_name`,value:n.drugName},{field:`indications_and_usage`,value:n.indication},{field:`openfda.substance_name`,value:n.activeIngredient},{field:`openfda.route`,value:n.route}],u=r(s),d=n.hasBoxedWarning?l(`_exists_:boxed_warning`):c(),p={search:a(u,d)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugLabels(p),p,e=>f(e,n.sections))}function m(e){return{productNdc:e.product_ndc,brandName:e.brand_name,genericName:e.generic_name,labelerName:e.labeler_name,dosageForm:e.dosage_form,route:e.route??[],activeIngredients:e.active_ingredients?.slice(0,3).map(e=>({name:e.name,strength:e.strength}))??[],marketingStartDate:e.marketing_start_date,productType:e.product_type}}async function h(n){t.tools(`searchDrugNDC`,n);let i=[{field:`product_ndc`,value:n.productNdc},{field:`brand_name`,value:n.brandName},{field:`generic_name`,value:n.genericName},{field:`labeler_name`,value:n.labelerName},{field:`dosage_form`,value:n.dosageForm},{field:`route`,value:n.route}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugNDC(a),a,m)}function g(e){return{recallNumber:e.recall_number,recallingFirm:e.recalling_firm,classification:e.classification,status:e.status,productDescription:s(e.product_description).orUndefined(),reasonForRecall:s(e.reason_for_recall).orUndefined(),recallInitiationDate:e.recall_initiation_date,distributionPattern:s(e.distribution_pattern).orUndefined(),city:e.city,state:e.state}}async function _(i){t.tools(`searchDrugEnforcement`,i);let s=[{field:`recalling_firm`,value:i.recallingFirm},{field:`classification`,value:i.classification},{field:`status`,value:i.status},{field:`state`,value:i.state}],c=r(s),l=n(`recall_initiation_date`,i.dateFrom,i.dateTo),u={search:a(c,l)||void 0,limit:i.limit,skip:i.skip};return o(await e.searchDrugEnforcement(u),u,g)}function v(e){let t=e.submissions?.[0];return{applicationNumber:e.application_number,sponsorName:e.sponsor_name,products:e.products?.slice(0,3).map(e=>({brandName:e.brand_name,dosageForm:e.dosage_form,route:e.route,marketingStatus:e.marketing_status,activeIngredients:e.active_ingredients?.slice(0,3).map(e=>({name:e.name,strength:e.strength}))??[]}))??[],latestSubmission:t?{type:t.submission_type,status:t.submission_status,statusDate:t.submission_status_date}:void 0}}async function y(n){t.tools(`searchDrugsFDA`,n);let i=[{field:`sponsor_name`,value:n.sponsorName},{field:`application_number`,value:n.applicationNumber},{field:`products.brand_name`,value:n.brandName},{field:`products.marketing_status`,value:n.marketingStatus}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugsFDA(a),a,v)}function b(e){return{genericName:e.generic_name,proprietaryName:e.proprietary_name,status:e.status,description:s(e.description).orUndefined(),initialPostingDate:e.initial_posting_date,resolvedShortageDate:e.resolved_shortage_date}}async function x(n){t.tools(`searchDrugShortages`,n);let i=[{field:`generic_name`,value:n.genericName},{field:`status`,value:n.status}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugShortages(a),a,b)}export{d as handleSearchDrugAdverseEvents,_ as handleSearchDrugEnforcement,p as handleSearchDrugLabels,h as handleSearchDrugNDC,x as handleSearchDrugShortages,y as handleSearchDrugsFDA};
|
|
2
2
|
//# sourceMappingURL=drug-handlers.js.map
|